使用根元素语法中的名称空间前缀解析XML-Java

时间:2018-09-19 13:29:48

标签: java xml xpath xml-namespaces

我有一个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'>不是 XPath-元素名称在此XML中有点重复)。部分代码如下:

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检查器测试相同的XPath和相同的XML文件会产生我想要的结果。它甚至可以使用

之类的XPath进行属性检索。
/semseg:Envelope/Triangle/Triangle/@time

似乎名称空间前缀存在问题。解析XML而没有任何名称空间前缀的情况对XPath来说很好。

2 个答案:

答案 0 :(得分:2)

您的XML输入实际上有两个名字空间。

默认名称空间

第一个是默认值,声明如下:

<semseg:Envelope ... xmlns="http://another-random-URL" ...

作为默认元素,任何没有名称空间的XML元素都属于该默认名称空间。

semseg名称空间

如此定义:

<semseg:Envelope xmlns:semseg="http://a-random-URL" ...

意味着每个带有semseg前缀的XML元素都属于该命名空间。

翻译您的要求

因此,您的目标是要针对

的XPath表达式
  • 任何Triangle元素(无前缀,因此实际上会转换为Triangle名称空间中的任何http://another-random-URL元素)。
  • 这是根semseg:Enveloppe元素的直接子元素(实际上转换为属于“ http://a-random-URL”命名空间的本地名称Enveloppe root元素)。

在XPath中对此进行编程。

我们创建一个NamespaceContext来描述我们正在使用的名称空间: 我定义了希望使用的前缀,并将其映射到名称空间。这些前缀将由XPath引擎使用。我地图:

  • main命名空间的http://a-random-URL前缀
  • secondary命名空间的http://another-random-URL前缀

使用我定义的映射,可以将您的需求转换为该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,guava(我认为)或其他可用的框架,则通常会遇到类似SimpleNamespaceContextMapBasedNamespaceContext之类的东西。可能是更好的选择。

答案 1 :(得分:1)

这对我有用

CREATE NONCLUSTERED INDEX NC_wsm_WorkOrderSchedule_tsrjobid ON wsm_WorkOrderSchedule (tsrjobid);