Pregunta

Tengo un archivo XML que comienza así:

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

Tendré que abrir muchos de estos archivos.Cada uno de ellos tiene un espacio de nombres diferente, pero solo tendrá un espacio de nombres a la vez (nunca encontraré dos espacios de nombres definidos en un archivo xml).

Usando XPath, me gustaría tener una forma automática de agregar el espacio de nombres dado al administrador de espacios de nombres.Hasta ahora, sólo pude obtener el espacio de nombres analizando el archivo xml, pero tengo una instancia de XPathNavigator y debería tener una forma agradable y limpia de obtener los espacios de nombres, ¿verdad?

-- O --

Dado que solo tengo un espacio de nombres, de alguna manera hago que XPath use el único que está presente en el xml, evitando así saturar el código al agregar siempre el espacio de nombres.

¿Fue útil?

Solución

Hay algunas técnicas que puedes probar;El que utilice dependerá exactamente de la información que necesite obtener del documento, de lo riguroso que desee ser y de lo conforme que sea la implementación XPath que esté utilizando.

Una forma de obtener el URI del espacio de nombres asociado con un prefijo particular es usar el namespace:: eje.Esto le dará un nodo de espacio de nombres cuyo nombre es el prefijo y cuyo valor es el URI del espacio de nombres.Por ejemplo, puede obtener el URI del espacio de nombres predeterminado en el elemento del documento mediante la ruta:

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

Es posible que pueda usarlo para configurar las asociaciones de espacios de nombres para su XPathNavigator.Tenga en cuenta, sin embargo, que el namespace:: axis es uno de esos rincones de XPath 1.0 que no siempre se implementa.

Una segunda forma de obtener ese URI de espacio de nombres es utilizar el namespace-uri() función en el elemento del documento (que usted ha dicho que siempre estará en ese espacio de nombres).La expresion:

namespace-uri(/*)

le dará ese espacio de nombres.

Una alternativa sería olvidarse de asociar un prefijo con ese espacio de nombres y simplemente hacer que su ruta esté libre de espacios de nombres.Puedes hacer esto usando el local-name() funciona siempre que necesite hacer referencia a un elemento cuyo espacio de nombres no conoce.Por ejemplo:

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

Podrías ir un paso más allá y probar el URI del espacio de nombres del elemento con el del elemento del documento, si realmente quisieras:

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

Una última opción, dado que el espacio de nombres parece no significar nada para usted, sería ejecutar su XML a través de un filtro que elimine los espacios de nombres.Entonces no tendrá que preocuparse en absoluto por ellos en su XPath.La forma más sencilla de hacerlo sería simplemente eliminar el xmlns atributo con una expresión regular, pero podrías hacer algo más complejo si necesitaras hacer otras tareas al mismo tiempo.

Otros consejos

Esta transformación xslt de 40 líneas proporciona toda la información útil sobre los espacios de nombres en un documento XML determinado.:

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

Cuando se aplica en el siguiente 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>

se produce el resultado deseado:

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

Desafortunadamente, XPath no tiene ningún concepto de "espacio de nombres predeterminado".Debe registrar espacios de nombres con prefijos con el contexto XPath y luego usar esos prefijos en sus expresiones XPath.Significa xpath muy detallado, pero es un defecto básico de XPath 1.Aparentemente XPath 2 solucionará esto, pero eso no le sirve de nada en este momento.

Le sugiero que examine mediante programación su documento XML para buscar el espacio de nombres, asocie ese espacio de nombres con un prefijo en el contexto XPath y luego use el prefijo en las expresiones XPath.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top