2008-09-23 6 views
35

J'ai un fichier XML qui commence comme ceci:Comment récupérer les espaces de noms dans les fichiers XML à l'aide Xpath

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

Je vais devoir ouvrir beaucoup de ces fichiers. Chacun d'entre eux a un espace de noms différent mais n'aura qu'un seul espace de noms à la fois (je ne trouverai jamais deux espaces de noms définis dans un fichier xml). Utilisation de XPath Je voudrais avoir un moyen automatique d'ajouter l'espace de noms donné au gestionnaire d'espace de noms. Jusqu'ici, je ne pouvais obtenir l'espace de noms qu'en analysant le fichier xml, mais j'ai une instance de XPathNavigator et il devrait y avoir un moyen agréable et propre d'obtenir les espaces de noms, n'est-ce pas?

- OU -

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

+0

ou avez-vous déjà: xmlns: myns = "namespace-uri" Aussi lisez-vous les fichiers dans leur intégralité dans un document DOM ou parser en utilisant quelque chose comme XmlValidatingReader? – Kev

+0

Ils sont toujours dans l'espace de noms par défaut. Je ne lis pas encore complètement le fichier, car je suis resté coincé dans cette affaire; Je suppose que je ne comprends pas complètement lorsque vous demandez "dans un document DOM ou analyser en utilisant quelque chose comme le XmlValidatingReader"; J'utiliserais XPath seulement pour lire le XML, est-ce mauvais? –

Répondre

76

Il existe quelques techniques que vous pouvez essayer; que vous utiliserez dépendra exactement des informations dont vous avez besoin pour sortir du document, de la rigueur que vous souhaitez obtenir et de la conformité de l'implémentation XPath que vous utilisez. Une façon d'obtenir l'URI de l'espace de noms associé à un préfixe particulier est d'utiliser l'axe namespace::. Cela vous donnera un nœud d'espace de noms dont le nom est le préfixe et dont la valeur est l'URI de l'espace de noms. Par exemple, vous pouvez obtenir l'espace de nom URI par défaut sur l'élément de document à l'aide du chemin:

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

Vous pourriez être en mesure d'utiliser pour mettre en place les associations d'espace de noms pour votre XPathNavigator. Soyez averti, cependant, que l'axe namespace:: est l'un de ces coins de XPath 1.0 qui n'est pas toujours implémenté. Une deuxième façon d'obtenir cet URI d'espace de noms est d'utiliser la fonction namespace-uri() sur l'élément de document (que vous avez dit sera toujours dans cet espace de noms). L'expression:

namespace-uri(/*) 

vous donnera cet espace de noms. Une alternative serait d'oublier d'associer un préfixe à cet espace de noms, et de rendre votre chemin sans espace de noms. Vous pouvez le faire en utilisant la fonction local-name() chaque fois que vous avez besoin de faire référence à un élément dont vous ne connaissez pas l'espace de noms.Par exemple:

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

Vous pouvez aller un peu plus loin et tester l'URI d'espace de nom de l'élément contre celui de l'élément de document, si vous vouliez vraiment:

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

Une dernière option, étant donné que l'espace de noms semble ne rien vouloir dire, serait d'exécuter votre XML à travers un filtre qui supprime les espaces de noms. Alors vous ne devrez pas vous inquiéter à leur sujet dans votre XPath du tout. La façon la plus simple de le faire serait simplement de supprimer l'attribut xmlns avec une expression régulière, mais vous pourriez faire quelque chose de plus complexe si vous deviez faire d'autres choses en même temps.

+2

Merci beaucoup pour votre réponse détaillée Il semble que je n'ai pas encore de réputation pour vous voter –

+1

une réponse exemplaire, merci, JeniT – kostja

+0

La deuxième méthode fonctionne bien dans Qt avec QXmlQuery. Très bonne réponse. –

4

Malheureusement, XPath n'a aucun concept de "namespace par défaut". Vous devez enregistrer des espaces de noms avec des préfixes avec le contexte XPath, puis utiliser ces préfixes dans vos expressions XPath. Cela signifie pour xpath très verbeux, mais c'est une lacune fondamentale de XPath 1. Apparemment XPath 2 répondra à cela, mais cela ne vous sert à rien pour le moment.

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

+0

Il semble qu'il faudra se résumer à ça ..! Merci –

+0

Je pense que c'est la réponse actuelle car il semble que le désir était d'éviter les complexités supplémentaires de l'interrogation d'un espace de noms dans XPath. Ne pas oublier d'accepter la réponse appropriée. – AnthonyWJones

10

Cette transformation xslt 40 en ligne fournit toutes les informations utiles sur les espaces de noms dans un document XML donné:

<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 désiré est produit:

Est-ce qu'ils sont toujours dans l'espace de noms par défaut?
+0

Comment appliquer ceci? –

Questions connexes