que ejemplos java xml xpath xml-namespaces

ejemplos - ¿Cómo consultar XML utilizando espacios de nombres en Java con XPath?



que es xpath java (6)

Cuando mi XML se ve así (sin xmlns ), entonces puedo consultarlo fácilmente con XPath like /workbook/sheets/sheet[1]

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <workbook> <sheets> <sheet name="Sheet1" sheetId="1" r:id="rId1"/> </sheets> </workbook>

Pero cuando se ve así, no puedo

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"> <sheets> <sheet name="Sheet1" sheetId="1" r:id="rId1"/> </sheets> </workbook>

¿Algunas ideas?


Asegúrese de estar haciendo referencia al espacio de nombres en su XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" >


En el segundo archivo XML de ejemplo, los elementos están vinculados a un espacio de nombres. Su XPath está tratando de abordar los elementos que están vinculados al espacio de nombres predeterminado "sin espacio de nombres", por lo que no coinciden.

El método preferido es registrar el espacio de nombres con un espacio de nombre-prefijo. Hace que tu XPath sea mucho más fácil de desarrollar, leer y mantener.

Sin embargo, no es obligatorio que registre el espacio de nombres y use el prefijo del espacio de nombres en su XPath.

Puede formular una expresión XPath que use una coincidencia genérica para un elemento y un filtro de predicado que restrinja la coincidencia para el local-name() deseado local-name() y el namespace-uri() . Por ejemplo:

/*[local-name()=''workbook'' and namespace-uri()=''http://schemas.openxmlformats.org/spreadsheetml/2006/main''] /*[local-name()=''sheets'' and namespace-uri()=''http://schemas.openxmlformats.org/spreadsheetml/2006/main''] /*[local-name()=''sheet'' and namespace-uri()=''http://schemas.openxmlformats.org/spreadsheetml/2006/main''][1]

Como puede ver, produce una declaración extremadamente larga y detallada de XPath que es muy difícil de leer (y mantener).

También podría simplemente coincidir con el local-name() del elemento e ignorar el espacio de nombres. Por ejemplo:

/*[local-name()=''workbook'']/*[local-name()=''sheets'']/*[local-name()=''sheet''][1]

Sin embargo, corre el riesgo de hacer coincidir los elementos incorrectos. Si su XML tiene vocabularios mixtos (que pueden no ser un problema para esta instancia) que usan el mismo local-name() , su XPath podría coincidir con los elementos incorrectos y seleccionar el contenido incorrecto:


He escrito una implementación simple de NamespaceContext ( here ), que toma un Map<String, String> como entrada, donde la key es un prefijo, y el value es un espacio de nombres.

Sigue la especificación de NamespaceContext , y puede ver cómo funciona en las pruebas unitarias .

Map<String, String> mappings = new HashMap<>(); mappings.put("foo", "http://foo"); mappings.put("foo2", "http://foo"); mappings.put("bar", "http://bar"); context = new SimpleNamespaceContext(mappings); context.getNamespaceURI("foo"); // "http://foo" context.getPrefix("http://foo"); // "foo" or "foo2" context.getPrefixes("http://foo"); // ["foo", "foo2"]

Tenga en cuenta que tiene una dependencia en Google Guava


Si está utilizando Spring, ya contiene org.springframework.util.xml.SimpleNamespaceContext.

import org.springframework.util.xml.SimpleNamespaceContext; ... XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath(); SimpleNamespaceContext nsc = new SimpleNamespaceContext(); nsc.bindNamespaceUri("a", "http://some.namespace.com/nsContext"); xpath.setNamespaceContext(nsc); XPathExpression xpathExpr = xpath.compile("//a:first/a:second"); String result = (String) xpathExpr.evaluate(object, XPathConstants.STRING);


Todos los espacios de nombres que tiene la intención de seleccionar en el código fuente XML deben estar asociados con un prefijo en el idioma del host. En Java / JAXP esto se hace especificando el URI para cada prefijo del espacio de nombres usando una instancia de javax.xml.namespace.NamespaceContext . Desafortunadamente, no hay implementación de NamespaceContext en el SDK.

Afortunadamente, es muy fácil escribir el tuyo:

import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.xml.namespace.NamespaceContext; public class SimpleNamespaceContext implements NamespaceContext { private final Map<String, String> PREF_MAP = new HashMap<String, String>(); public SimpleNamespaceContext(final Map<String, String> prefMap) { PREF_MAP.putAll(prefMap); } public String getNamespaceURI(String prefix) { return PREF_MAP.get(prefix); } public String getPrefix(String uri) { throw new UnsupportedOperationException(); } public Iterator getPrefixes(String uri) { throw new UnsupportedOperationException(); } }

Úselo así:

XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); HashMap<String, String> prefMap = new HashMap<String, String>() {{ put("main", "http://schemas.openxmlformats.org/spreadsheetml/2006/main"); put("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"); }}; SimpleNamespaceContext namespaces = new SimpleNamespaceContext(prefMap); xpath.setNamespaceContext(namespaces); XPathExpression expr = xpath .compile("/main:workbook/main:sheets/main:sheet[1]"); Object result = expr.evaluate(doc, XPathConstants.NODESET);

Tenga en cuenta que aunque el primer espacio de nombre no especifica un prefijo en el documento de origen (es decir, es el espacio de nombre predeterminado ) , debe asociarlo con un prefijo de todos modos . Su expresión debe hacer referencia a los nodos en ese espacio de nombres usando el prefijo que ha elegido, como este:

/main:workbook/main:sheets/main:sheet[1]

Los nombres de prefijo que elija asociar con cada espacio de nombre son arbitrarios; no es necesario que coincidan con lo que aparece en el código fuente XML. Esta asignación es solo una forma de decirle al motor de XPath que un nombre de prefijo dado en una expresión se correlaciona con un espacio de nombre específico en el documento de origen.


Tu problema es el espacio de nombres predeterminado. Consulte este artículo sobre cómo tratar los espacios de nombres en su XPath: http://www.edankert.com/defaultnamespaces.html

Una de las conclusiones que extraen es:

Entonces, para poder usar expresiones XPath en contenido XML definido en un espacio de nombres (predeterminado), necesitamos especificar un mapeo de prefijo de espacio de nombres

Tenga en cuenta que esto no significa que tenga que cambiar su documento de origen de ninguna manera (aunque puede poner los prefijos de espacio de nombres allí si así lo desea). Suena extraño, ¿verdad? Lo que hará es crear una asignación de prefijo de espacio de nombres en su código de Java y usar dicho prefijo en su expresión de XPath. Aquí, crearemos una asignación de spreadsheet de spreadsheet a su espacio de nombre predeterminado.

XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); // there''s no default implementation for NamespaceContext...seems kind of silly, no? xpath.setNamespaceContext(new NamespaceContext() { public String getNamespaceURI(String prefix) { if (prefix == null) throw new NullPointerException("Null prefix"); else if ("spreadsheet".equals(prefix)) return "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; else if ("xml".equals(prefix)) return XMLConstants.XML_NS_URI; return XMLConstants.NULL_NS_URI; } // This method isn''t necessary for XPath processing. public String getPrefix(String uri) { throw new UnsupportedOperationException(); } // This method isn''t necessary for XPath processing either. public Iterator getPrefixes(String uri) { throw new UnsupportedOperationException(); } }); // note that all the elements in the expression are prefixed with our namespace mapping! XPathExpression expr = xpath.compile("/spreadsheet:workbook/spreadsheet:sheets/spreadsheet:sheet[1]"); // assuming you''ve got your XML document in a variable named doc... Node result = (Node) expr.evaluate(doc, XPathConstants.NODE);

Y listo ... Ahora tienes tu elemento guardado en la variable de result .

Advertencia: si está analizando su XML como un DOM con las clases JAXP estándar, asegúrese de llamar a setNamespaceAware(true) en su DocumentBuilderFactory . De lo contrario, este código no funcionará.