Mehr von Jürgen Gutsch

Mehr von Jürgen Gutsch

Empfehlungen von Jürgen Gutsch

Blog-Empfehlungen von Jürgen Gutsch

Willkommen bei ASP.NET Zone. Anmelden | Registrieren | Hilfe

Jürgen Gutsch

ASP.NET und mehr...

News

LINQ to XML: Queries auf XML mit Namespaces

Beim Zugriff auf XML mit bestimmten XML-Namespaces kommt es beim herkömmlichen Queries zu einem Fehler. Wie auch beim XMLDoxument muss beim den Abfragen auf bestimmte Knoten ein Namespace angegeben werden.

Ausgangspunkt bei mir ist eine Abfrage per WebDAV auf einen Exchange Server, bei der ich ungefähr folgende Antwort erhalte:
<?xml version="1.0" ?>
<a:multistatus
    xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/"
    xmlns:d="urn:schemas:httpmail:"
    xmlns:c="xml:"
    xmlns:a="DAV:">
  <a:response>
    <a:href>http://mdomain/exchange/Account/</a:href> 
    <a:propstat> 
      <a:status>HTTP/1.1 200 OK</a:status> 
      <a:prop>
      </a:prop>
    </a:propstat>
  </a:response>
</a:multistatus>

Folgende Abfrage liefert einen Fehler:
XDocument xdoc = XDocument.Load(xmlResponseReader);
XElement xelement = xdoc.Element("a:multistatus");

XmlException: The ':' character, hexadecimal value 0x3A, cannot be included in a name.

Um das Element nun fehlerfrei zu erhalten muss es wie folgt abgefragt werden:
XDocument xdoc = XDocument.Load(xmlResponseReader);
XElement xelement = xdoc.Element("{DAV:}multistatus");

Damit haben wir den – aus meiner Sicht - ersten und einzigen, aber recht großen Nachteil von LINQ to XML gefunden: Die Namespaces müssen direkt in den Namen des Elements eingegeben werden.

Die Lösung die in der MSDN angeboten wird, ist auch nicht besser:
XDocument xdoc = XDocument.Load(xmlResponseReader);
XNamespace a = "DAV:";
XElement xelement = xdoc.Element(a + "multistatus");

Mir stellt sich hier die Frage: Wieso erstelle ich ein XNamespace-Object wenn ich es dann doch per String-Gebastel in den Elementnamen einbauen muss. Der Kommentator in der MSDN Dokumentation ist ganz offensichtlich der selben Meinung.

Peter hat für dieses Problem eine kleine elegante Lösung, indem er für die Methoden Element und Elements jeweils eine Extension Methode geschrieben hat:
public static IEnumerable<XElement> Elements(
    this XContainer source,
    XNamespace ns,
    string name)
{
  return source.Elements(ns + name);
}
public static XElement Element(
    this XContainer source,
    XNamespace ns,
    string name)
{
  return source.Element(ns + name);
}

So kann ich wenigstens außerhalb der Extension Methoden ohne String-Gebastel auskommen:
XDocument xdoc = XDocument.Load(xmlResponseReader);
XNamespace a = "DAV:";
XElement xelement = xdoc.Element(a, "multistatus");

René hat mich per Kommentar darauf hingewiesen, dass man die Elemente natürlich auch per XName abfragen kann. Das ganze sieht dann so aus:
XDocument xdoc = XDocument.Load(xmlResponseReader); 
XName xname = XName.Get("multistatus", "DAV:");
XElement xelement = xdoc.Element(xname);

DotNetKicks-DE Image
Posted: Donnerstag, 13. August 2009 11:30 von Jürgen Gutsch

Kommentare

dotnet-kicks.de sagte:

Sie wurden gekickt (eine gute Sache) - Trackback von dotnet-kicks.de

# August 13, 2009 11:39

Peter Bucher sagte:

Ja das ist echt ein richtig doofer und nervender Nachteil, um es mal - entgegen diesem auch passenden verlinken Kommentar - gelinde zu sagen ;-)

# August 13, 2009 11:51

Rene Drescher-Hackel sagte:

Hallo Jürgen,

wo ist das Problem?

XDocument doc = XDocument.Load(Server.MapPath("gutsch.xml"));

XElement ele = doc.Root; // liefert den Knoten 'a:multistatus'

XElement resp = ele.Element(XName.Get("response", "DAV:")); // liefert den Knoten 'a:response'

ich sehe da keinen Nachteil ;-)

Gruß

Rene

# August 13, 2009 20:20

Jürgen Gutsch sagte:

Autsch...

Danke, René, für den Hinweis. Mit dem XName sieht das natürlich wieder ganz anders aus.

# August 13, 2009 21:53

Rene Drescher-Hackel sagte:

Aber ich kenne das, man denkt: 'Mist, da gibt es keine gescheite Lösung für mein Problem', und fängt an um die Ecke zu denken. Und wenn man meint, man hat 'die Lösung' gefunden, kommt einer daher und sagt: 'jo, nimm doch die Methode xy aus dem Framework'

Aber so lernt man in zweifacher Hinsicht: zum einem hat man sich mit dem Thema mal beschäftigt und zum anderen hat man wieder eine der zig-Tausend Frameworkmethoden kennen gelernt.

# August 14, 2009 08:07

Jürgen Gutsch sagte:

Ja, da hast du recht. Man lernt halt nie aus :-)

Oder wie der Kollege Henry Ford sagte: "Wer aufhört zu lernen, ist alt. Er mag zwanzig sein oder achtzig."

# August 14, 2009 08:51

Peter Bucher sagte:

Hallo Rene

Danke für deine Anmerkung.

Ich sehe da aber immer noch den Nachteil, das es so Redundanzen gibt.

Okay, ich gebe ja zu, man könnte den / die Namespace Strings auch noch auslagern.

Allerdings finde ich dann die Schreibweise mit den Extensionmethods um einiges eleganter.

Wieso nicht gleich so, oder geht das nur mir so?

# August 14, 2009 17:49

Rene Drescher-Hackel sagte:

@Peter: leider finde ich deinen Artikel zu dem Thema nicht, um dazu was sagen zu können. Ich für meinen Teil setze vorrangig Extensionmethods ein - nur im konkreten Fall - der vom Jürgen beschrieben wurde - ist es überflüssig, da die Klasse XName eine statische Methode ja bereitstellt.

@Jürgen: XName xname = XName.Get("multistatus", "DAV:"); ist meines Wissens nicht im Sinne des CCD. Wozu muss ich eine Variable deklarieren, wenn doch die Klasse eine statische Methode bereitstellt?

Gruß

Rene

# August 15, 2009 10:48

Peter Bucher sagte:

Hallo Rene

Ich habe keinen Artikel dazu.

Im konkreten Fall könnte man das (@Jürgen) XName.Get auch so nehmen, da stimme ich Rene bei.

Die Extensionmethods finde ich in diesem Fall eleganter und wenn man XName.Get() benutzt, dann sollte man den Namespace String zumindest auslagern, sonst entstehen Redundanzen.

# August 15, 2009 12:52

Jürgen Gutsch sagte:

@René, im Gegenteil, eine separate Variable zu deklarieren, ist eher Clean Code, als alles in eine Zeile zu Backen. Clean Code heißt, den Code lesbar zu gestalten. Das einzige was an den drei Zeilen IMO nicht CC ist, ist die Benahmung der Objekte.

# August 16, 2009 07:28

Rene Drescher-Hackel sagte:

Jürgen ich widerspreche dir ungern, aber der Gedanke, Code lesbarer zu gestalten wird nicht dadurch verletzt, indem du eine statische Methode als solche auch aufrufst.

XName.Get - sagt genau das aus, was es tut. Dadurch, dass du es einer weiteren Variablen vorher zuweist, wird der Code nicht lesbarer sondern eher unnötig aufgebläht.

Anders ist die Sache zu beurteilen, wenn du die Variable 'xname' mehr als einmal im Zugriff benötigen würdest, was im konkreten Anwendungsfall aber nicht zutrifft. Daneben ist das von dir angeführte Beispiel das Root-Element des XML. Hinsichtlich der Lesbarkeit von Code ist es also überflüssig, diesen über

XName xname = XName.Get("multistatus", "DAV:");

XElement xelement = xdoc.Element(xname);

abzurufen.

Dem Gedanken des CCD folgend würde hier folgende Zeilem genügen

XDocument xmlDokument= XDocument.Load(xmlResponseReader);

XElement rootElement = xmlDokument.Root;

und der Code ist lesbar.

Gruß

Rene

# August 16, 2009 11:19

Jürgen Gutsch sagte:

Sorry, René, aber bitte lies das Buch "Clean Code" von Robert C. Martin ;-)

XName.Get() ist jetzt noch ein relativ kurzes Statement, also grenzwertig. Aber Um Code lesbar zu halten hilft es auch die Statemants pro Zeile zu reduzieren, also pro Zeile nur eine Aufgabe zu lösen. Setzt du XName.Get() direkt in die Abfrage des XElements hast du dort zwei Aufgaben (XName erstellen und XElement abfragen). Ob jetzt Code überflüssig ist oder nicht, ist dann irrelevant, wenn der Code dadurch lesbarer wird.

Du siehst sicher ein, dass Code sehr unleserlich werden kann, wenn man alles in eine Zeile packt.

Uncle Bob (Robert C. Martin) schreibt in seinem Buch, dass es sinnvoller ist eine Zeile mehr Code zu schreiben, als alles in in eine Zeile zu schreiben.

# August 17, 2009 09:30

Peter Bucher sagte:

Hallo ihr zwei

Irgendwie stehe ich zwischen euch, denn das Beispiel ist schon sehr grenzwertig ;-)

# August 17, 2009 10:37

Jürgen Gutsch sagte:

@Peter, das es grenzwertig ist hab ich ja geschrieben ;-)

# August 17, 2009 11:03

Rene Drescher-Hackel sagte:

Hallo Jürgen,

ich muss deinen Kommentar doch noch einmal aufgreifen:

Prinzipiell muss man doch differenzieren:

Habe ich zwei Aufgaben in einer Zeile und der Code ist dabei verständlichm, lesbar und was weis ich noch alles, werde ich sicher den Teufel tun und das ganze auf zwei Zeilen aufblähen. Das ist nämlich auch "Clean Code" - also sauberer Code.

Anders sieht die Sache aus, wenn ich z.B. komplexere Abfrage in Form einer verkürzten IF-Anweisung in einer Zeile unterbringe wie z.B.

var a = c ? d : g==h ? j : l!= m ? N : o;

Das ist natürlich schwer lesbar, gleich es funktioniert.

In Bezug auf statische Methode bleibe ich aber bei der Aussage, dass wenn ich den Wert nur 1 mal benötige, dann rufe ich die Methode direkt auf, andernfalls nutze ich einen zusätzlichen Parameter.

Mein Verständnis von "Clean Code" fängt da an, wo der Code optimiert ist, funktionell korrekt und verständlich. Wenn ich nur den Schwerpunkt auf Verständlichkeit lege, dann hat das wenig mit "Clean Code" im eigentlichen Sinne zu tun.

Gruß

Rene

# August 18, 2009 10:01

ralfw sagte:

Ich versteh leider jetzt nicht mehr so recht, worum es eigentlich geht.

Geht es um die Eigenheiten eines XML API? Oder geht es um CCD?

Aus CCD-Sicht sage ich mal:

CCD ersetzt nicht die Kenntnis eines API. Ob

XDocument xdoc = XDocument.Load(xmlResponseReader);

XElement xelement = xdoc.Element("{DAV:}multistatus");

wirklich viel umständlicher ist

XDocument xdoc = XDocument.Load(xmlResponseReader);

XNamespace ns = "DAV:";

XElement xelement = xdoc.Element(ns, "multistatus");

wage ich eigentlich nicht zu beurteilen.

Mir schiene jedoch eine Ext Meth, der ich keinen Namespace übergeben muss, noch geeigneter:

XDocument xdoc = XDocument.Load(xmlResponseReader);

XElement xelement = xdoc.Element("DAV:", "multistatus");

Aber einerlei. An CCD wurde die Frage gestellt, ob 1 Statement pro Zeile angezeigt sei.

Meine Meinung dazu: da ist unter dem Radar von CCD. Eine Anweisung oder mehrere... das kommt auf die Anweisungen an. Ich schreibe z.B. gern sowas wie:

i=0; j=1; k=2;

Das sind mehrere Anweisungen - aber eine Sinneinheit. Die Initialisierungen sind kurz und knapp und können so als Block gut zusammengefasst werden.

Ob

A;

B;

C;

oder

A; B; C;

ist also von der Länge der Anweisungen abhängig. Was der eine oder andere für lesbar hält, fällt in die Nähe eines Geschmacksurteils wie auch

i=0;

vs.

i = 0;

Das Gesamtbild muss stimmen. Und das kann im Detail dann auch mal unterschiedlich aussehen.

Ich wäre zwar nicht allzu traurig, wenn C# nicht mehr als eine Anweisung pro Zeile zuließe, doch ich nehme gern auch die Möglichkeit mit, dass es mehrere sein dürfen.

Wichtiger finde ich zwei andere Dimensionen:

-Zeilenlänge

-Schachtelungstiefe

Eine Zeile mit wievielen Anweisungen auch immer sollte nicht über eine normale Editorfensterbreite hinausgehen. Wenn man scrollen muss, dann sinkt die Verständlichkeit bzw. die Aufnahmegeschwindigkeit rapide.

Und ich finde Schachtelungen beobachtenswert. Es ist ein Unterschied, ob ich

A(B(C()));

oder

var c=C();

var b=B(c);

A(b);

schreibe. Letzteres macht mehr Aufwand, ist aber flüssiger zu lesen, weil man von oben nach unten lesen kann.

Bei der Schachtelung kehrt sich die Leserichtung quasi um: man muss von innen nach außen, d.h. eher von rechts nach links lesen. Das hakt schnell mal. Da hilft auch eine andere Schreibweise nicht viel wie

A(

 B(

   C()

  )

);

Deshalb bin ich ja ein Freund von Flow-APIs oder allgm Fluent Interfaces. Die machen nicht nur bei async Verarbeitung das Verständnis und die Notation einfacher.

Wer die Variablen b und c scheut, der könnte doch vielleicht so lesbarer schreiben:

C | B | A

Das ist in C# natürlich nicht so recht möglich. Wie wäre es jedoch mit:

Flow

.From(C)

.To(B)

.To(A)

.Execute();

Sieht für so ein kleines Beispiel etwas überkandidelt aus. Aber wenn die Szenarien größer werden, halte ich das für ein unterschätztes Programmiermodell.

Noch ein Wort zu statischen Methoden, die in der Diskussion auftauchten. Wenn die im API drinstecken, dann kann man nix machen. Sollten wir selbst aber statische Methoden schreiben? Eher nicht. (Auch wenn R# immer mal wieder vorschlägt, eine Methode statisch zu machen.) Der Grund: static methods don´t compose.

Da statische Methoden keine Interfacemethoden implementieren können, kann man ihre Funktionalität nicht gut injizieren. (Von Funktionszeigern darauf sehe ich mal ab.)

Just my 2c.

-Ralf

# August 19, 2009 09:20

Jürgen Gutsch sagte:

Hallo Zusammen, ich werde in Kürze meinen Standpunkt zum Thema als separaten Blogpost veröffentlichen. Damit die Kommentare nicht zu einem Forum mutieren, können wir auch gerne im Forum selber zum Thema Diskutieren. Ich werde unter Sonstiges einen Thread eröffnen, sobald der andere Blogpost publiziert ist.

@Ralf, Vielen Dank für deinen ausführlichen Kommentar :-)

# August 20, 2009 08:49

Jürgen Gutsch sagte:

Aus meinem letzten Blogpost entwickelte sich eine interessante Diskussion zum Thema Clean Code . Im Grunde

# August 20, 2009 11:20
Anonyme Kommentare sind nicht zugelassen