Ich bin mir ziemlich sicher , warum - zumindest im .Net Framework - bei der Ausführung von XPath-Abfragen ein verwendet werden XmlNamespaceManager
muss, um Namespaces (oder das eher klobige und ausführliche [local-name()=...
XPath-Prädikat / Funktion / was auch immer) zu verarbeiten . Ich kann verstehen , warum Namespaces sind notwendig oder zumindest vorteilhaft, aber warum ist es so kompliziert?
Um ein einfaches XML-Dokument abzufragen (keine Namespaces) ...
<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode>
<nodeName>Some Text Here</nodeName>
</rootNode>
... man kann so etwas verwenden doc.SelectSingleNode("//nodeName")
(was passen würde <nodeName>Some Text Here</nodeName>
)
Rätsel Nr. 1 : Mein erster Ärger - wenn ich das richtig verstehe - ist, dass ich lediglich einen Namespace-Verweis auf das übergeordnete / Root-Tag hinzufüge (unabhängig davon, ob es als Teil eines untergeordneten Knoten-Tags verwendet wird oder nicht).
<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode xmlns="http://example.com/xmlns/foo">
<nodeName>Some Text Here</nodeName>
</rootNode>
... erfordert mehrere zusätzliche Codezeilen, um das gleiche Ergebnis zu erzielen:
Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("ab", "http://example.com/xmlns/foo")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//ab:nodeName", nsmgr)
... sich im Wesentlichen ein nicht existierendes Präfix (" ab
") ausdenken, um einen Knoten zu finden, der nicht einmal ein Präfix verwendet. Wie macht das Sinn? Was ist (konzeptionell) falsch doc.SelectSingleNode("//nodeName")
?
Rätsel Nr. 2 : Angenommen, Sie haben ein XML-Dokument, das Präfixe verwendet:
<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode xmlns:cde="http://example.com/xmlns/foo" xmlns:feg="http://example.com/xmlns/bar">
<cde:nodeName>Some Text Here</cde:nodeName>
<feg:nodeName>Some Other Value</feg:nodeName>
<feg:otherName>Yet Another Value</feg:otherName>
</rootNode>
... Wenn ich das richtig verstehe, müssten Sie beide Namespaces zum hinzufügen XmlNamespaceManager
, um eine Abfrage für einen einzelnen Knoten durchzuführen ...
Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("cde", "http://example.com/xmlns/foo")
nsmgr.AddNamespace("feg", "http://example.com/xmlns/bar")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//feg:nodeName", nsmgr)
... Warum brauche ich in diesem Fall (konzeptionell) einen Namespace-Manager?
****** ANONYMISIERT in Kommentare unten ****
Bearbeiten Hinzugefügt: Meine überarbeitete und verfeinerte Frage basiert auf der offensichtlichen Redundanz des XmlNamespaceManager in den meiner Meinung nach meisten Fällen und der Verwendung des Namespace-Managers zur Angabe einer Zuordnung des Präfixes zum URI:
Wenn die direkte Zuordnung des Namespace-Präfixes ("cde") zum Namespace-URI (" http://example.com/xmlns/foo ") im Quelldokument explizit angegeben ist:
...<rootNode xmlns:cde="http://example.com/xmlns/foo"...
Was ist die konzeptionelle Notwendigkeit für einen Programmierer, diese Zuordnung neu zu erstellen, bevor eine Abfrage durchgeführt wird?
quelle
doc.SelectSingleNode("//feg:nodeName")
- und damit fertig zu sein? Kann es für das menschliche Gehirn Zweifel geben, was mit diesem Codefragment gemeint ist? [PARAGRAPH] Anders ausgedrückt , was trägt wirklich zum Verständnis der Situation durch die zusätzlichen Codezeilen und die Instanziierung eines XmlNamespaceManager bei, der nicht eindeutig vom XML-Quelldokument und / oder der XPath-Abfrage abgeleitet werden kann?AddNamespace()
? Ich kann nicht anders, als zu glauben, dass mir etwas Offensichtliches fehlt, und wenn ja, bitte klären Sie mich auf!xmlns:abc="..." xmlns:def="..."
Attributen. WarumXPathNodeIterator
um alles in der Welt kann man nicht herausfinden, welcher Namespace mit einem untergeordneten Knoten wie<abc:SomeNode/>
ohne verknüpft istXmlNamespaceManager
?Antworten:
Der grundlegende Punkt (wie oben von Kev ausgeführt) ) ist, dass der Namespace-URI der wichtige Teil des Namespace ist und nicht das Namespace-Präfix, sondern eine "willkürliche Annehmlichkeit".
Ich kann mir zwei Gründe vorstellen, warum Sie einen Namespace-Manager benötigen, anstatt dass es etwas Magisches gibt, das mit dem Dokument funktioniert.
Grund 1
Wenn es erlaubt wäre, dem documentElement nur Namespace-Deklarationen hinzuzufügen, wie in Ihren Beispielen, wäre es für selectSingleNode in der Tat trivial, nur das zu verwenden, was definiert ist.
Sie können jedoch Namespace-Präfixe für jedes Element in einem Dokument definieren, und Namespace-Präfixe sind nicht eindeutig an einen bestimmten Namespace in einem Dokument gebunden. Betrachten Sie das folgende Beispiel
<w xmlns:a="mynamespace"> <a:x> <y xmlns:a="myOthernamespace"> <z xmlns="mynamespace"> <b:z xmlns:b="mynamespace"> <z xmlns="myOthernamespace"> <b:z xmlns:b="myOthernamespace"> </y> </a:x> </w>
In diesem Beispiel, was würden Sie wollen
//z
,//a:z
und//b:z
auf Rückkehr? Wie würden Sie das ohne einen externen Namespace-Manager ausdrücken?Grund 2
Sie können denselben XPath-Ausdruck für jedes gleichwertige Dokument wiederverwenden, ohne etwas über die verwendeten Namespace-Präfixe wissen zu müssen.
doc1:
<x> <z:y xmlns:z="mynamespace" /> </x>
doc2:
<x xmlns"mynamespace"> <y> </x>
Um dieses letztere Ziel ohne einen Namespace-Manager zu erreichen, müssten Sie jedes Dokument überprüfen und für jedes einen benutzerdefinierten XPath-Ausdruck erstellen.
quelle
//z
sollte übereinstimmen<z xmlns="mynamespace">
und<z xmlns="myOthernamespace">
,//a:z
würde eine leere Menge zurück und//b:z
würde passen<b:z xmlns:b="mynamespace">
und<b:z xmlns:b="myOthernamespace">
- die Logik dahinter ist , dass es keine Namespace - Manager war angegeben, und es gab keine " versuchen , zu erhalten Informationen aus dem Dokument selbst "Befehl, daher werden Namespaces wie alle anderen Attribute behandelt und werden:
zu einem anderen gültigen Zeichen, wie-
in meinem Kopf, wenn Sie Ihre Daten kennen oder es Ihnen egal ist, sollte das Abfragen eines Knotens nicht so schmerzhaft seinDer Grund ist einfach. Es ist keine Verbindung zwischen den Präfixen, die Sie in Ihrer XPath-Abfrage verwenden, und den deklarierten Präfixen im XML-Dokument erforderlich. Um ein Beispiel zu geben, sind die folgenden xmls semantisch äquivalent:
<aaa:root xmlns:aaa="http://someplace.org"> <aaa:element>text</aaa:element> </aaa:root>
vs.
<bbb:root xmlns:bbb="http://someplace.org"> <bbb:element>text</bbb:element> </bbb:root>
Die
ccc:root/ccc:element
Abfrage " " stimmt mit beiden Instanzen überein, sofern im Namespace-Manager eine Zuordnung dafür vorhanden ist.Die .NET-Implementierung kümmert sich nicht um die in der XML verwendeten Literalpräfixe, sondern nur darum, dass für das Abfrageliteral ein Präfix definiert ist und der Namespace-Wert mit dem tatsächlichen Wert des Dokuments übereinstimmt. Dies ist erforderlich, um konstante Abfrageausdrücke zu haben, auch wenn die Präfixe zwischen den verwendeten Dokumenten variieren und dies die richtige Implementierung für den allgemeinen Fall ist.
quelle
Soweit ich das beurteilen kann, gibt es keinen guten Grund, warum Sie eine manuell definieren müssen
XmlNamespaceManager
abc
beurteilen , um an Knoten mit Präfix zu gelangen , wenn Sie ein Dokument wie dieses haben:<itemContainer xmlns:abc="http://abc.com" xmlns:def="http://def.com"> <abc:nodeA>...</abc:nodeA> <def:nodeB>...</def:nodeB> <abc:nodeC>...</abc:nodeC> </itemContainer>
Microsoft konnte sich einfach nicht die Mühe machen, etwas zu schreiben, um zu erkennen, dass
xmlns:abc
es bereits in einem übergeordneten Knoten angegeben wurde. Ich könnte mich irren, und wenn ja, würde ich Kommentare zu dieser Antwort begrüßen, damit ich sie aktualisieren kann.Jedoch, Blog-Beitrag scheint jedoch meinen Verdacht zu bestätigen. Grundsätzlich heißt es, dass Sie eine manuell definieren
XmlNamespaceManager
und manuell durchlaufen müssenxmlns:
Attribut , wobei Sie jedes Attribut dem Namespace-Manager hinzufügen müssen. Keine Ahnung, warum Microsoft dies nicht automatisch tun konnte.Hier ist eine Methode, die ich basierend auf diesem Blog-Beitrag erstellt habe, um automatisch eine
XmlNamespaceManager
basierend auf denxmlns:
Attributen einer Quelle zu generierenXmlDocument
:/// <summary> /// Creates an XmlNamespaceManager based on a source XmlDocument's name table, and prepopulates its namespaces with any 'xmlns:' attributes of the root node. /// </summary> /// <param name="sourceDocument">The source XML document to create the XmlNamespaceManager for.</param> /// <returns>The created XmlNamespaceManager.</returns> private XmlNamespaceManager createNsMgrForDocument(XmlDocument sourceDocument) { XmlNamespaceManager nsMgr = new XmlNamespaceManager(sourceDocument.NameTable); foreach (XmlAttribute attr in sourceDocument.SelectSingleNode("/*").Attributes) { if (attr.Prefix == "xmlns") { nsMgr.AddNamespace(attr.LocalName, attr.Value); } } return nsMgr; }
Und ich benutze es so:
XPathNavigator xNav = xmlDoc.CreateNavigator(); XPathNodeIterator xIter = xNav.Select("//abc:NodeC", createNsMgrForDocument(xmlDoc));
quelle
:
ähnlich einer Zahl, einem Buchstaben oder-
und wird wird alsoprfx:NodeName
genauso behandelt wieprfxNodeName
oderprfx-NodeName
- eine einfache Kennung ... obwohl nicht standardkonform ... seufzIch antworte auf Punkt 1:
Das Festlegen eines Standard-Namespace für ein XML-Dokument bedeutet weiterhin, dass die Knoten auch ohne Namespace-Präfix, dh:
<rootNode xmlns="http://someplace.org"> <nodeName>Some Text Here</nodeName> </rootNode>
befinden sich nicht mehr im "leeren" Namespace. Sie benötigen noch eine Möglichkeit, diese Knoten mit XPath zu referenzieren. Daher erstellen Sie ein Präfix, um auf sie zu verweisen, auch wenn es "erfunden" ist.
Um Punkt 2 zu beantworten:
<rootNode xmlns:cde="http://someplace.org" xmlns:feg="http://otherplace.net"> <cde:nodeName>Some Text Here</cde:nodeName> <feg:nodeName>Some Other Value</feg:nodeName> <feg:otherName>Yet Another Value</feg:otherName> </rootNode>
Intern im Instanzdokument werden die Knoten, die sich in einem Namespace befinden, mit ihrem Knotennamen und ihrem langen Namespace-Namen gespeichert. Dies wird (im W3C-Sprachgebrauch) als erweiterter Name bezeichnet .
Zum Beispiel
<cde:nodeName>
wird im Wesentlichen als gespeichert<http://someplace.org:nodeName>
. Ein Namespace-Präfix ist eine willkürliche Annehmlichkeit für Menschen, sodass wir Folgendes nicht tun müssen, wenn wir XML eingeben oder lesen müssen:<rootNode> <http://someplace.org:nodeName>Some Text Here</http://someplace.org:nodeName> <http://otherplace.net:nodeName>Some Other Value</http://otherplace.net:nodeName> <http://otherplace.net:otherName>Yet Another Value</http://otherplace.net:otherName> </rootNode>
Wenn ein XML-Dokument durchsucht wird, wird es nicht nach dem benutzerfreundlichen Präfix durchsucht. Die Suche erfolgt nach dem Namespace-URI, sodass Sie XPath über eine mit übergebene Namespace-Tabelle über Ihre Namespaces informieren müssen
XmlNamespaceManager
.quelle
doc.SelectSingleNode("//nodeName", NamespaceFlags.UseDocumentNamespace)
XmlNamespaceManager
stattdessen einen zu übergeben ?XmlNamespaceManager
ist eine, in der es mehrere Namespaces ("someplace.org" UND "otherplace.net") gibt, die dieselben Präfixe verwenden (beide verwendenxmlns:place
oder ähnlich, aber in unterschiedlichen Bereichen des Dokuments). Andernfalls liefern das Dokument und die Abfrage alle Informationen, die zur Erzielung des gewünschten Ergebnisses erforderlich sind.//feg:nodeName
einen bestimmten Knoten zu finden? Es sollte relativ trivial sein, intern zu konvertierenfeg
... ohne dasshttp://otherplace.net
ich diese Beziehung explizit angeben muss - sie befindet sich genau dort im Stammknoten! (xmlns:feg="http://otherplace.net"
). Zumindest denke ich, dass es eine Hilfsfunktion geben sollte wieXmlNamespaceManager.GetNSFromDocument(xdoc)
... Wenn die Antwort einfach ist, dass sie diese Arbeit (noch) nicht für Sie erledigt haben, dann OK! Ist das der Fall?Sie müssen die URI / Präfix-Paare in der XmlNamespaceManager-Instanz registrieren, damit SelectSingleNode () weiß, auf welchen bestimmten "nodeName" -Knoten Sie sich beziehen - den von "http://someplace.org" oder den von "http:": //otherplace.net ".
Bitte beachten Sie, dass der konkrete Präfixname bei der XPath-Abfrage keine Rolle spielt. Ich glaube, das funktioniert auch:
SelectSingleNode () benötigt lediglich eine Verbindung zwischen dem Präfix Ihres XPath-Ausdrucks und dem Namespace-URI.
quelle
Dieser Thread hat mir geholfen, das Problem der Namespaces viel klarer zu verstehen. Vielen Dank. Als ich Jez 'Code sah , versuchte ich es, weil es nach einer besseren Lösung aussah, als ich programmiert hatte. Ich habe jedoch einige Mängel entdeckt. Wie geschrieben, sieht es nur im Stammknoten aus (Namespaces können jedoch überall aufgelistet werden.) Und es werden keine Standard-Namespaces verarbeitet. Ich habe versucht, diese Probleme durch Ändern seines Codes zu beheben, aber ohne Erfolg.
Hier ist meine Version dieser Funktion. Es verwendet reguläre Ausdrücke, um die Namespace-Zuordnungen in der gesamten Datei zu finden. arbeitet mit Standard-Namespaces und gibt ihnen das beliebige Präfix 'ns'; und behandelt mehrere Vorkommen desselben Namespace.
quelle