Frage

Ich habe eine XML-Datei, die wie folgt beginnt:

<Elements name="Entities" xmlns="XS-GenerationToolElements">

Ich werde eine Menge dieser Dateien öffnen müssen. Jeder von ihnen hat einen anderen Namespace, aber hat nur einen Namensraum zu einem Zeitpunkt (Ich werde nie zwei Namespaces in einer XML-Datei definiert finden).

Mit XPath Ich möchte einen automatischen Weg, um den gegebenen Namespace auf den Namespace-Manager hinzuzufügen. Bisher kann ich nur den Namensraum erhalten, indem die XML-Datei Parsen aber ich habe eine XPathNavigator-Instanz und es soll eine schöne und saubere Art und Weise muß die Namensräume zu bekommen, nicht wahr?

- oder -

Da ich nur einen Namensraum haben, irgendwie XPath machen das nur eine, die in der XML vorhanden ist, damit vermieden wird, den Code unübersichtlich durch den Namespace immer angehängt wird.

War es hilfreich?

Lösung

Es gibt ein paar Techniken, die Sie könnten versuchen; die Sie verwenden, um auf genau abhängen, welche Informationen Sie benötigen aus dem Dokument zu erhalten, wie streng Sie sein wollen, und wie konforme die XPath-Implementierung Sie verwenden ist.

Eine Möglichkeit, den Namespace-URI mit einem bestimmten Präfix assoziiert zu erhalten, ist die namespace:: Achse verwendet wird. Dadurch erhalten Sie einen Namespace-Knoten, dessen Name das Präfix und dessen Wert der Namespace-URI. Zum Beispiel könnten Sie den Standard-Namespace-URI auf dem Dokumentelement mit dem Weg bekommen:

/*/namespace::*[name()='']

Sie könnten in der Lage sein, das verwenden, um die Namespace-Zuordnungen für Ihren XPathNavigator einzurichten. Seien Sie gewarnt, aber, dass die namespace:: Achse einer dieser Ecken von XPath 1.0, die nicht immer umgesetzt wird.

Ein zweiter Weg, um diesen Namensraum des Erhaltens URI ist die namespace-uri() Funktion auf dem Dokumentelement zu verwenden (was Sie gesagt haben immer in diesem Namensraum sein). Der Ausdruck:

namespace-uri(/*)

geben Sie diesen Namespace.

Eine Alternative wäre etwa Zuordnen eines Präfix mit diesem Namensraum vergessen und nur deinen Weg Namespace-frei machen. Sie können dies tun, indem Sie die local-name()-Funktion, wenn Sie auf ein Element verweisen müssen, deren Namespace Sie nicht wissen. Zum Beispiel:

//*[local-name() = 'Element']

Sie können einen Schritt weiter gehen und die Namespace URI des Elements testen gegen die des Dokumentelements, wenn Sie wirklich wollte:

//*[local-name() = 'Element' and namespace-uri() = namespace-uri(/*)]

Eine letzte Möglichkeit gegeben, dass der Namespace Ihnen nichts zu bedeuten scheint, wäre Ihre XML durch einen Filter laufen zu lassen, die die Namespaces abstreift. Dann werden Sie nicht alles über sie in Ihrem XPath zu kümmern. Der einfachste Weg, dies zu tun wäre, einfach das xmlns Attribut mit einem regulären Ausdruck zu entfernen, aber man kann etwas komplexer tun, wenn Sie in der gleichen Zeit aufräumt andere tun müssen.

Andere Tipps

Diese 40-line XSLT-Transformation liefert alle notwendigen Informationen über die Namespaces in einem bestimmten XML-Dokument :

<xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:ext="http://exslt.org/common"
   exclude-result-prefixes="ext"
>

<xsl:output omit-xml-declaration="yes" indent="yes"/>

<xsl:strip-space elements="*"/>

<xsl:key name="kNsByNsUri" match="ns" use="@uri"/>

<xsl:variable name="vXmlNS" 
    select="'http://www.w3.org/XML/1998/namespace'"/>

<xsl:template match="/">
  <xsl:variable name="vrtfNamespaces">
    <xsl:for-each select=
      "//namespace::*
             [not(. = $vXmlNS)
             and
              . = namespace-uri(..)
           ]">
      <ns element="{name(..)}"
          prefix="{name()}" uri="{.}"/>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="vNamespaces"
    select="ext:node-set($vrtfNamespaces)/*"/>

  <namespaces>
          <xsl:for-each select=
           "$vNamespaces[generate-id()
                        =
                         generate-id(key('kNsByNsUri',@uri)[1])
                        ]">
            <namespace uri="{@uri}">
              <xsl:for-each select="key('kNsByNsUri',@uri)/@element">
                <element name="{.}" prefix="{../@prefix}"/>
              </xsl:for-each>
            </namespace>
          </xsl:for-each>
  </namespaces>
</xsl:template>

Wenn auf dem folgenden XML-Dokument angewendet:

<a xmlns="my:def1" xmlns:n1="my:n1"
   xmlns:n2="my:n2" xmlns:n3="my:n3">
  <b>
    <n1:d/>
  </b>
  <n1:c>
    <n2:e>
      <f/>
    </n2:e>
  </n1:c>
  <n2:g/>
</a>

das gewünschte Ergebnis erzeugt:

<namespaces>
   <namespace uri="my:def1">
      <element name="a" prefix=""/>
      <element name="b" prefix=""/>
      <element name="f" prefix=""/>
   </namespace>
   <namespace uri="my:n1">
      <element name="n1:d" prefix="n1"/>
      <element name="n1:c" prefix="n1"/>
   </namespace>
   <namespace uri="my:n2">
      <element name="n2:e" prefix="n2"/>
      <element name="n2:g" prefix="n2"/>
   </namespace>
</namespaces>

Leider XPath hat kein Konzept von „Standard-Namespace“. Sie müssen Namespaces mit Präfixen mit dem XPath-Kontext registrieren, und dann diese Präfixe in Ihrem XPath-Ausdrücke verwenden. Es bedeutet für sehr ausführlichen XPath, aber es ist ein grundsätzlicher Mangel von XPath 1. Scheinbar XPath 2 werden diese Adresse, aber das ist nichts für Sie jetzt.

Ich schlage vor, dass Sie programmatisch Ihr XML-Dokument für den Namensraum untersuchen, dass Namespace mit einem Präfix im XPath-Kontext verknüpfen, dann das Präfix in dem XPath-Ausdrücke verwenden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top