I have an XML
of the form:
<?xml version="1.0" encoding="UTF-8"?>
<semseg:Envelope xmlns:semseg="http://a-random-URL" xmlns="http://another-random-URL">
<semseg:subject>Subject</semseg:subject>
<semseg:Sender>
<semseg:name>Me</semseg:name>
</semseg:Sender>
<Triangle>
<Triangle time='2017-11-29'>
<Triangle key='a' value='b'/>
<Triangle key='c' value='d'/>
<Triangle key='e' value='f'/>
<Triangle key='g' value='h'/>
</Triangle>
</Triangle>
</semseg:Envelope>
And I am trying to retrieve the element <Triangle>
( not <Triangle time='2017-11-29'>
- element names are a bit repetitive in this XML) using XPath
. Part of the code is the following:
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = documentBuilder.parse("file.xml");
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
XPathExpression xpr = xPath.compile("/semseg:Envelope/Triangle");
NodeList nodes = (NodeList)xpr.evaluate(doc, XPathConstants.NODESET);
I have tried many possible combinations for the XPath
without any luck unfortunately since no elements are selected. Nevertheless, testing the same XPath
with this online XPath
checker and the same XML
file yields exactly the results I am looking for. It evens works for attribute retrieval using XPaths like
/semseg:Envelope/Triangle/Triangle/@time
Seems like there is a problem with the namespace prefixes. Parsing XML
s without any namespace prefixes works just fine with XPath
.
Your XML input actually has two namepsaces.
The first is the default one, declared as such :
<semseg:Envelope ... xmlns="http://another-random-URL" ...
Being the default one, any XML element that has no namespace on it belongs to this default namespace.
Defined as such :
<semseg:Envelope xmlns:semseg="http://a-random-URL" ...
Meaning every XML element prefixed with semseg
belongs to this namespace.
So you're aiming at an XPath expression that will target
Triangle
element (no prefix, so that actually translates to any Triangle
element from the http://another-random-URL
namespace ). semseg:Enveloppe
element (that actually translates to a root element of the local name Enveloppe
belonging to the " http://a-random-URL " namespace ). We create a NamespaceContext that describes what namespaces we are working with : I define prefixes that I wish to work with, and map them to the namespaces. These prefixes will be used by the XPath engine. I map :
main
prefix to the http://a-random-URL
namespace secondary
prefix to the http://another-random-URL
namespace Using this mapping that I defined, I can translate your requirement to this XPath :
/main:Envelope/secondary:Triangle
And this works :
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
xPath.setNamespaceContext(new NamespaceContext() {
@Override
public String getNamespaceURI(String prefix) {
if ("main".equals(prefix)) {
return "http://a-random-URL";
}
if ("secondary".equals(prefix)) {
return "http://another-random-URL";
}
return null;
}
@Override
public String getPrefix(String namespaceURI) {
// This should be implemented but I'm lazy and this sample works without it
return null;
}
@Override
public Iterator getPrefixes(String namespaceURI) {
// This should be implemented but I'm lazy and this sample works without it
return null;
}
});
XPathExpression xpr = xPath.compile("/main:Envelope/secondary:Triangle");
NodeList nodes = (NodeList)xpr.evaluate(doc, XPathConstants.NODESET);
System.out.println(nodes.getLength());
Outputs :
1
Here I have implemented a really dumb namespace context, but if you hava Spring framework, CXF, guava (I think), or other frameworks at reach, you often have something like SimpleNamespaceContext
or MapBasedNamespaceContext
that are probably better options.
这对我有用
/\\*[local-name()='Envelope']/\\*[local-name()='Triangle']/\\*[local-name()='Triangle']/@time
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.