2008-09-23 20 views
35

Tengo un archivo XML que comienza así:Como recuperar espacios de nombres de archivos XML utilizando XPath

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

Voy a tener que abrir una gran cantidad de estos archivos. Cada uno de estos 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 manera automática de agregar el espacio de nombres dado al administrador del espacio de nombres. Hasta ahora, solo podía 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 -

Teniendo en cuenta que sólo tengo un espacio de nombres, de alguna manera hacer uso de XPath el único que está presente en el XML, evitando así que saturan el código por siempre añadiendo el espacio de nombres.

+0

¿Están siempre en el espacio de nombres predeterminado? o alguna vez tiene: xmlns: myns = "namespace-uri" ¿También lee los archivos en su totalidad en un documento DOM o analiza usando algo como el XmlValidatingReader? – Kev

+0

Siempre están en el espacio de nombre predeterminado. Todavía no leo el archivo por completo, ya que me quedé atascado en este asunto; Supongo que no entiendo completamente cuando preguntas "en un documento DOM o analiza utilizando algo como XmlValidatingReader"; Usaría XPath solo para leer el XML; ¿es malo? –

Respuesta

76

Existen algunas técnicas que puede probar; que utilice dependerá exactamente de la información que necesita para salir del documento, cuán rigurosa quiere ser y cuán conforme es la implementación de XPath que está utilizando.

Una forma de obtener el URI del espacio de nombres asociado con un prefijo en particular es utilizando el eje namespace::. 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, se puede obtener el espacio de nombres URI por defecto en el elemento de documento utilizando la ruta:

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

es posible que pueda utilizar para establecer que las asociaciones de espacio de nombres para su XPathNavigator. Tenga en cuenta, sin embargo, que el eje namespace:: es una de esas esquinas de XPath 1.0 que no siempre se implementa.

Una segunda manera de conseguir ese URI de espacio es el uso de la función namespace-uri() en el elemento de documento (que usted ha dicho siempre estará en ese espacio de nombres). La expresión:

namespace-uri(/*) 

le dará ese espacio de nombre.

Una alternativa sería olvidarse de asociar un prefijo con ese espacio de nombres, y simplemente hacer su ruta libre de espacio de nombres. Puede hacerlo utilizando la función local-name() siempre que necesite hacer referencia a un elemento cuyo espacio de nombres no conozca.Por ejemplo:

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

Usted podría ir un paso más allá y probar el URI de espacio del elemento contra la del elemento de documento, si realmente quería:

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

Una última opción, teniendo en cuenta 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ás que preocuparte por ellos en tu XPath en absoluto. La forma más sencilla de hacerlo sería simplemente eliminar el atributo xmlns con una expresión regular, pero podría hacer algo más complejo si necesitara hacer otra limpieza al mismo tiempo.

+2

Muchas gracias por su respuesta detallada Parece que todavía no tengo una reputación para votar –

+1

una respuesta ejemplar, gracias, JeniT – kostja

+0

El segundo método funciona bien en Qt con QXmlQuery. Gran respuesta. –

4

Desafortunadamente, XPath no tiene ningún concepto de "espacio de nombres predeterminado". Necesita registrar espacios de nombres con prefijos con el contexto XPath, y luego usar esos prefijos en sus expresiones XPath. Significa para xpath muy detallado, pero es una deficiencia básica de XPath 1. Aparentemente XPath 2 abordará esto, pero eso no te sirve en este momento.

Sugiero que analice programáticamente su documento XML para el espacio de nombres, asocie ese espacio de nombres con un prefijo en el contexto XPath, luego use el prefijo en las expresiones xpath.

+0

¡Parece que tendrá que reducirse a eso ...! Gracias –

+0

Sospecho que esta es la respuesta real ya que parece ser el deseo era evitar las complejidades adicionales de consultar un espacio de nombres en XPath. No olvides aceptar la respuesta adecuada. – AnthonyWJones

10

Esta transformación XSLT 40 de línea proporciona toda la información útil sobre los espacios de nombres en un documento XML dado:

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

¿Cómo aplicar esto? –

Cuestiones relacionadas