2009-05-27 18 views
12

La resolución de un xpath que incluye espacios de nombres en Java parece requerir el uso de un objeto NamespaceContext, asignando prefijos a las URL del espacio de nombres y viceversa. Sin embargo, no puedo encontrar ningún mecanismo para obtener un NamespaceContext que no sea implementarlo yo mismo. Esto parece contrario a la intuición.Espacio de nombres Contexto y uso de espacios de nombres con XPath

La pregunta: ¿Hay alguna forma fácil de adquirir un NamespaceContext de un documento, o para crear uno, o en su defecto, a renunciar por completo prefijos y especifique el XPath con nombres completos?

+1

Hay algunos errores básicos de su pregunta: puede obtener todos los URI de espacio de nombres de un XML con XPath (hay respuestas para esto en SO), pero no puede llenar una clase de enlace URI de prefijo-espacio de nombres porque * * para cada nodo ** puede ser un límite diferente. Al seleccionar nodos, debe saber de antemano los nombres de sus elementos deseados ** y eso incluye su URI de espacio de nombres ** –

Respuesta

11

Es posible obtener una instancia de NamespaceContext sin escribir su propia clase. Su página class-use muestra que puede obtener una utilizando el paquete javax.xml.stream.

String ctxtTemplate = "<data xmlns=\"http://base\" xmlns:foo=\"http://foo\" />"; 
NamespaceContext nsContext = null; 

XMLInputFactory factory = XMLInputFactory.newInstance(); 
XMLEventReader evtReader = factory 
    .createXMLEventReader(new StringReader(ctxtTemplate)); 
while (evtReader.hasNext()) { 
    XMLEvent event = evtReader.nextEvent(); 
    if (event.isStartElement()) { 
    nsContext = ((StartElement) event) 
     .getNamespaceContext(); 
    break; 
    } 
} 

System.out.println(nsContext.getNamespaceURI("")); 
System.out.println(nsContext.getNamespaceURI("foo")); 
System.out.println(nsContext 
    .getNamespaceURI(XMLConstants.XMLNS_ATTRIBUTE)); 
System.out.println(nsContext 
    .getNamespaceURI(XMLConstants.XML_NS_PREFIX)); 

prefijos anterior es del todo probable que conduzca a expresiones ambiguas - si usted quiere eliminar los prefijos de espacio de nombres, que había necesidad de cambiar el formato del documento. Crear un contexto a partir de un documento no necesariamente tiene sentido. Los prefijos tienen que coincidir con los utilizados en la expresión XPath, no los que están en cualquier documento, como en este código:

String xml = "<data xmlns=\"http://base\" xmlns:foo=\"http://foo\" >" 
    + "<foo:value>" 
    + "hello" 
    + "</foo:value>" 
    + "</data>"; 
String expression = "/stack:data/overflow:value"; 
class BaseFooContext implements NamespaceContext { 
    @Override 
    public String getNamespaceURI(String prefix) { 
    if ("stack".equals(prefix)) 
     return "http://base"; 
    if ("overflow".equals(prefix)) 
     return "http://foo"; 
    throw new IllegalArgumentException(prefix); 
    } 

    @Override 
    public String getPrefix(String namespaceURI) { 
    throw new UnsupportedOperationException(); 
    } 

    @Override 
    public Iterator<String> getPrefixes(
     String namespaceURI) { 
    throw new UnsupportedOperationException(); 
    } 
} 
XPathFactory factory = XPathFactory.newInstance(); 
XPath xpath = factory.newXPath(); 
xpath.setNamespaceContext(new BaseFooContext()); 
String value = xpath.evaluate(expression, 
    new InputSource(new StringReader(xml))); 
System.out.println(value); 

Ni la aplicación devuelto por la API StAX ni la de arriba implementar la plena class/method contratos como se define en el documento. Puede obtener una implementación completa basada en mapas here.

+0

Este tipo de confirma mis miedos. No puedo tener un NamespaceContext que haga una asignación arbitraria a menos que implemente la clase yo mismo. Pero el ejemplo de las secuencias XML al menos me da una ruta para crear un NamespaceContext a partir de una función estática que crea un micro documento como lo hace en el primer ejemplo. Voy a intentar eso. – Jherico

+0

Sí, es un poco molesto que no haya una implementación predeterminada. – McDowell

+0

Yo digo que eso es estúpido. ¿Cuántas otras interfaces Java que se utilizan en las API públicas no tienen implementaciones o fábricas concretas disponibles públicamente? – Jherico

7

He estado trabajando con el uso de xpath y NamespaceContexts yo mismo. Encontré un buen tratamiento del problema on developerworks.

+2

Después de leer este ensayo de IBM, puedo entender por qué no hay una implementación predeterminada. Hay una variedad de formas de resolver este problema. Dicho esto: ¿Por qué un grupo de código abierto, como Apache, no crea una biblioteca de diferentes implementaciones? – kevinarpe

6

Encontré una implementación conveniente en "Apache WebServices Common Utilities" llamado NamespaceContextImpl.

Usted puede usar la siguiente dependencia experto para obtener esta clase:

<dependency> 
    <groupId>org.apache.ws.commons</groupId> 
    <artifactId>ws-commons-util</artifactId> 
    <version>1.0.1</version> 
</dependency> 

he usarlo de la manera siguiente (Sé que es construido para el saxofón, pero después de leer el código, está bien):

NamespaceContextImpl nsContext = new NamespaceContextImpl(); 
nsContext.startPrefixMapping("foo", "my.name.space.com"); 

No es necesario llamar a endPrefixMapping.

+1

¡todavía está codificando los espacios de nombres! – Meitham

+0

Exactamente lo que estaba buscando. El javadoc: http://ws.apache.org/commons/util/apidocs/index.html –

+0

Meitham - No de ninguna manera se evita la codificación del espacio de nombres. Está iniciando la búsqueda de XPath creando este contexto de espacio de nombres, inicializándolo mapeando un prefijo corto en cada espacio de nombre * que usted conoce * contenido en el documento, y luego cuando busca por XPath usa esos prefijos al definir la ruta. –

1

Si está utilizando el marco de primavera puede volver a utilizar su aplicación NamespaceContext org.springframework.util.xml.SimpleNamespaceContext

Ésta es una respuesta similar como el de Asaf Mesika. Por lo tanto, no le da un NamespaceContext automático basado en su documento. Tienes que construirlo tú mismo. Aún así te ayuda porque al menos te da una implementación para empezar.

Cuando nos enfrentábamos a un problema similar, funcionó tanto el resorte SimpleNamespaceContext como los "Apache WebServices Common Utilities". Queríamos evitar la dependencia de jar adicional en Apache WebServices Common Utilities y usamos Spring, porque nuestra aplicación está basada en Spring.

0

Si está utilizando Jersey 2 y sólo tiene un espacio de nombres XML predeterminado (xmlns="..."), puede utilizar SimpleNamespaceResolver:

<?xml version="1.0" encoding="UTF-8"?> 
<Outer xmlns="http://host/namespace"> 
    <Inner /> 
</Outer> 
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); 
docBuilderFactory.setNamespaceAware(true); 
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); 

Document document = docBuilder.parse(new File("document.xml")); 
String query = "/t:Outer/t:Inner"; 

XPath xpath = XPathFactory.newInstance().newXPath(); 
String xmlns = document.getDocumentElement().getAttribute("xmlns"); 
xpath.setNamespaceContext(new SimpleNamespaceResolver("t", xmlns)); 

NodeList nodeList = (NodeList) xpath.evaluate(query, document, XPathConstants.NODESET); 

//nodeList will contain the <Inner> element 

También puede especificar xmlns manualmente si lo desea.

Cuestiones relacionadas