Domanda

Ho un file XML che inizia in questo modo:

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

Dovrò aprire molti di questi file. Ognuno di questi ha uno spazio dei nomi diverso ma avrà solo uno spazio dei nomi alla volta (non troverò mai due spazi dei nomi definiti in un file xml).

Usando XPath mi piacerebbe avere un modo automatico per aggiungere lo spazio dei nomi dato al gestore dello spazio dei nomi. Finora, ho potuto ottenere lo spazio dei nomi solo analizzando il file xml ma ho un'istanza XPathNavigator e dovrebbe avere un modo semplice e pulito per ottenere gli spazi dei nomi, giusto?

- OPPURE -

Dato che ho un solo spazio dei nomi, in qualche modo faccio in modo che XPath usi l'unico che è presente nell'xml, evitando così di ingombrare il codice aggiungendo sempre lo spazio dei nomi.

È stato utile?

Soluzione

Ci sono alcune tecniche che potresti provare; che usi dipenderà esattamente da quali informazioni hai bisogno per uscire dal documento, quanto rigoroso vuoi essere e quanto sia conforme l'implementazione XPath che stai usando.

Un modo per ottenere l'URI dello spazio dei nomi associato a un prefisso particolare è utilizzare l'asse namespace :: . Questo ti darà un nodo dello spazio dei nomi il cui nome è il prefisso e il cui valore è l'URI dello spazio dei nomi. Ad esempio, è possibile ottenere l'URI dello spazio dei nomi predefinito sull'elemento del documento utilizzando il percorso:

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

Potresti essere in grado di usarlo per impostare le associazioni dello spazio dei nomi per XPathNavigator. Attenzione, tuttavia, che l'asse namespace :: è uno di quegli angoli di XPath 1.0 che non è sempre implementato.

Un secondo modo per ottenere quell'URI dello spazio dei nomi è usare la funzione namespace-uri () sull'elemento del documento (che hai detto sarà sempre in quello spazio dei nomi). L'espressione:

namespace-uri(/*)

ti darà quello spazio dei nomi.

Un'alternativa sarebbe dimenticare di associare un prefisso a quello spazio dei nomi e rendere il proprio percorso libero dallo spazio dei nomi. Puoi farlo usando la funzione local-name () ogni volta che devi fare riferimento a un elemento di cui non conosci lo spazio dei nomi. Ad esempio:

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

Potresti fare un altro passo e testare l'URI dello spazio dei nomi dell'elemento rispetto a quello dell'elemento del documento, se lo desideri davvero:

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

Un'ultima opzione, dato che lo spazio dei nomi sembra non significare nulla per te, sarebbe quella di eseguire il tuo XML attraverso un filtro che elimina gli spazi dei nomi. Quindi non dovrai preoccuparti di loro nel tuo XPath. Il modo più semplice per farlo sarebbe semplicemente quello di rimuovere l'attributo xmlns con un'espressione regolare, ma potresti fare qualcosa di più complesso se avessi bisogno di fare altri riordini allo stesso tempo.

Altri suggerimenti

Questa trasformazione xslt a 40 righe fornisce tutte le informazioni utili sugli spazi dei nomi in un determinato documento 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>

Se applicato sul seguente documento XML:

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

viene prodotto il risultato desiderato:

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

Sfortunatamente, XPath non ha alcun concetto di " spazio dei nomi predefinito " ;. È necessario registrare spazi dei nomi con prefissi con il contesto XPath e quindi utilizzare tali prefissi nelle espressioni XPath. Significa per xpath molto dettagliato, ma è un difetto di base di XPath 1. Apparentemente XPath 2 lo affronterà, ma non è utile in questo momento.

Suggerisco di esaminare a livello di codice il documento XML per lo spazio dei nomi, associare quello spazio dei nomi a un prefisso nel contesto XPath, quindi utilizzare il prefisso nelle espressioni xpath.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top