在根元素的语法中使用名称空间前缀解析 XML - Java
Parse XML using namespace prefixes in syntax for root elements - Java
我有一个 XML
的形式:
<?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>
我正在尝试检索元素 <Triangle>
( 不是 <Triangle time='2017-11-29'>
- 在此 XML 中元素名称有点重复)使用 XPath
。部分代码如下:
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);
我已经为 XPath
尝试了许多可能的组合,不幸的是没有任何运气,因为没有选择任何元素。尽管如此,使用 this 在线 XPath
检查器和相同的 XML
文件测试相同的 XPath
会产生我正在寻找的结果。它甚至可以使用像
这样的 XPath 进行属性检索
/semseg:Envelope/Triangle/Triangle/@time
名称空间前缀似乎有问题。解析没有任何命名空间前缀的 XML
s 对 XPath
.
工作得很好
这对我有用
/\*[local-name()='Envelope']/\*[local-name()='Triangle']/\*[local-name()='Triangle']/@time
您的 XML 输入实际上有两个命名空间。
默认命名空间
第一个是默认的,声明如下:
<semseg:Envelope ... xmlns="http://another-random-URL" ...
作为默认命名空间,任何 XML 上没有命名空间的元素都属于此默认命名空间。
semseg 命名空间
这样定义:
<semseg:Envelope xmlns:semseg="http://a-random-URL" ...
意味着每个 XML 前缀为 semseg
的元素都属于这个命名空间。
翻译您的要求
所以您的目标是一个 XPath 表达式,它将以
为目标
- 任何
Triangle
元素(无前缀,因此实际上转换为 来自 http://another-random-URL
命名空间 的任何 Triangle
元素)。
- 这是根
semseg:Enveloppe
元素的直接子元素(实际上转换为属于“http://a-random-URL 的本地名称 Enveloppe
的 根元素" 命名空间).
在 XPath 中对此进行编程。
我们创建一个 NamespaceContext 来描述我们正在使用的命名空间:
我定义了我希望使用的前缀,并将它们映射到命名空间。这些前缀将由 XPath 引擎使用。我映射:
http://a-random-URL
命名空间的 main
前缀
http://another-random-URL
命名空间的 secondary
前缀
使用我定义的这个映射,我可以将您的要求转换为这个 XPath:
/main:Envelope/secondary:Triangle
这有效:
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());
输出:
1
我在这里实现了一个非常愚蠢的命名空间上下文,但是如果你有 Spring 框架、CXF、番石榴(我认为)或其他可用的框架,你通常会有 SimpleNamespaceContext
或 MapBasedNamespaceContext
可能是更好的选择。
我有一个 XML
的形式:
<?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>
我正在尝试检索元素 <Triangle>
( 不是 <Triangle time='2017-11-29'>
- 在此 XML 中元素名称有点重复)使用 XPath
。部分代码如下:
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);
我已经为 XPath
尝试了许多可能的组合,不幸的是没有任何运气,因为没有选择任何元素。尽管如此,使用 this 在线 XPath
检查器和相同的 XML
文件测试相同的 XPath
会产生我正在寻找的结果。它甚至可以使用像
/semseg:Envelope/Triangle/Triangle/@time
名称空间前缀似乎有问题。解析没有任何命名空间前缀的 XML
s 对 XPath
.
这对我有用
/\*[local-name()='Envelope']/\*[local-name()='Triangle']/\*[local-name()='Triangle']/@time
您的 XML 输入实际上有两个命名空间。
默认命名空间
第一个是默认的,声明如下:
<semseg:Envelope ... xmlns="http://another-random-URL" ...
作为默认命名空间,任何 XML 上没有命名空间的元素都属于此默认命名空间。
semseg 命名空间
这样定义:
<semseg:Envelope xmlns:semseg="http://a-random-URL" ...
意味着每个 XML 前缀为 semseg
的元素都属于这个命名空间。
翻译您的要求
所以您的目标是一个 XPath 表达式,它将以
为目标- 任何
Triangle
元素(无前缀,因此实际上转换为 来自http://another-random-URL
命名空间 的任何Triangle
元素)。 - 这是根
semseg:Enveloppe
元素的直接子元素(实际上转换为属于“http://a-random-URL 的本地名称Enveloppe
的 根元素" 命名空间).
在 XPath 中对此进行编程。
我们创建一个 NamespaceContext 来描述我们正在使用的命名空间: 我定义了我希望使用的前缀,并将它们映射到命名空间。这些前缀将由 XPath 引擎使用。我映射:
http://a-random-URL
命名空间的main
前缀http://another-random-URL
命名空间的secondary
前缀
使用我定义的这个映射,我可以将您的要求转换为这个 XPath:
/main:Envelope/secondary:Triangle
这有效:
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());
输出:
1
我在这里实现了一个非常愚蠢的命名空间上下文,但是如果你有 Spring 框架、CXF、番石榴(我认为)或其他可用的框架,你通常会有 SimpleNamespaceContext
或 MapBasedNamespaceContext
可能是更好的选择。