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);