40 votes

Comment faire pour récupérer des espaces de noms dans des fichiers XML à l'aide de Xpath

J'ai un fichier XML qui commence comme ceci:

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

Je vais avoir à ouvrir un grand nombre de ces fichiers. Chacun d'eux a un espace de noms différent, mais n'ont qu'un seul espace de noms à un moment (je ne vais jamais trouver deux espaces de noms définis dans un fichier xml).

À l'aide de XPath j'aimerais avoir un moyen automatique pour ajouter l'espace de noms donné à l'espace de noms du gestionnaire. Jusqu'à présent, je n'ai pu avoir de l'espace de noms par l'analyse du fichier xml, mais j'ai un XPathNavigator exemple et il doit avoir une belle et propre façon d'obtenir les espaces de noms, non?

-- OU --

Étant donné que je n'ai qu'un seul espace de noms, faire en quelque sorte XPath utiliser le seul qui est présent dans le fichier xml, évitant ainsi d'encombrer le code en ajoutant de l'espace de noms.

90voto

JeniT Points 3030

Il existe quelques techniques que vous pouvez essayer; que vous utilisez dépendra exactement quelles informations vous avez besoin de sortir de ce document, la rigueur avec laquelle vous voulez être, et de façon conforme à la XPath application que vous utilisez.

Une façon d'obtenir de l'URI d'espace de noms associé avec un préfixe particulier est à l'aide de l' namespace:: de l'axe. Cela vous donnera un nœud d'espace de noms dont le nom est le préfixe et dont la valeur est l'URI d'espace de noms. Par exemple, vous pourriez obtenir de l'URI d'espace de noms par défaut sur l'élément de document en utilisant le chemin d'accès:

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

Vous pourriez être en mesure de l'utiliser pour définir l'espace de noms d'associations pour votre XPathNavigator. Soyez averti, cependant, que l' namespace:: axe est l'un de ces coins de XPath 1.0 qui n'est pas toujours mis en œuvre.

Une deuxième façon d'obtenir des URI d'espace de noms est d'utiliser l' namespace-uri() de la fonction sur l'élément de document (que vous avez dit sera toujours dans l'espace de nom). L'expression:

namespace-uri(/*)

vous donnera l'espace de noms.

Une alternative serait d'oublier d'associer un préfixe de l'espace de nom, et faites votre chemin d'accès de l'espace de noms gratuit. Vous pouvez le faire en utilisant l' local-name() fonction lorsque vous avez besoin de se référer à un élément dont l'espace de noms que vous ne connaissez pas. Par exemple:

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

Vous pouvez aller plus loin et de tester l'URI d'espace de noms de l'élément à l'encontre l'un de l'élément de document, si vous le voulez bien:

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

Une dernière option, étant donné que l'espace de noms semble ne signifient rien pour vous, serait de exécuter votre XML au travers d'un filtre qui supprime les espaces de noms. Ensuite, vous n'aurez pas à vous soucier de votre XPath à tous. La meilleure façon de le faire serait tout simplement de supprimer l' xmlns d'attribut avec une expression régulière, mais vous pouvez faire quelque chose de plus complexe si vous avez besoin de faire d'autres à ranger dans le même temps.

12voto

Dimitre Novatchev Points 147842

Ce 40 de la ligne de transformation xslt fournit toutes les informations utiles sur les espaces de noms dans un document XML:

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

Lorsqu'il est appliqué sur le document XML suivant:

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

le résultat souhaité est obtenu:

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

4voto

skaffman Points 197885

Malheureusement, XPath n'est pas le concept de "espace de noms par défaut". Vous devez vous inscrire avec des préfixes d'espaces de noms avec XPath contexte, puis l'utilisation de ces préfixes dans vos expressions XPath. Cela signifie pour les très prolixe xpath, mais c'est une lacune de XPath 1. Apparemment XPath 2 permettra de résoudre ce problème, mais ce n'est pas utiliser pour l'instant.

Je vous suggère par programmation examiner votre document XML pour l'espace de noms, l'associer avec un préfixe d'espace de noms dans le XPath contexte, puis utilisez le préfixe dans les expressions xpath.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X