解析XML文件以获取所有命名空间信息

时间:2012-07-11 16:54:47

标签: java xml xslt xpath xquery

我希望能够从给定的XML文件中获取所有命名空间信息。

例如,如果输入XML文件类似于:

<ns1:create xmlns:ns1="http://predic8.com/wsdl/material/ArticleService/1/">
   <ns1:article xmlns:ns1="xmlns:ns1='http://predic8.com/material/1/">
      <ns1:id>1</ns1:id>
      <description>bar</description>
      <name>foo</name>
      <ns1:price>
         <amount>00.00</amount>
         <currency>USD</currency>
      </ns1:price>
      <ns1:price>
         <amount>11.11</amount>
         <currency>AUD</currency>
      </ns1:price>
   </ns1:article>
   <ns1:article xmlns:ns1="xmlns:ns1='http://predic8.com/material/1/">
      <ns1:id>2</ns1:id>
      <description>some name</description>
      <name>some description</name>
      <ns1:price>
         <amount>00.01</amount>
         <currency>USD</currency>
      </ns1:price>
   </ns1:article>
</ns1:create>

我希望输出看起来像这样(在这种情况下以逗号分隔):

create, ns1, http://predic8.com/wsdl/material/ArticleService/1/
article, ns1, http://predic8.com/material/1/
price, ns1, http://predic8.com/material/1/
id, ns1, http://predic8.com/material/1/

重要说明:

重要的是我们还要考虑在特定命名空间内定义的子节点,但其定义可以在更高的节点定义。例如,我们仍然想要选择节点ns1:id,我们需要追溯到父节点ns1:article以发现命名空间URL为xmlns:ns1='http://predic8.com/material/1/

我在Java中实现,所以我不介意基于Java的解决方案,甚至基于XSLT的解决方案看起来都是合适的。

3 个答案:

答案 0 :(得分:5)

我会使用内置的XMLStreamReader,它是流式XML解析器实现的接口(从XMLInputFactory类获取)。它的getName方法返回一个QName,它可以为您提供所需的一切。

有些事情:

File file = new File("samples/sample11.xml");
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
XMLStreamReader reader = inputFactory.createXMLStreamReader(new FileInputStream(file));
Set<String> namespaces = new HashSet<String>();
while (reader.hasNext()) {
      int evt = reader.next();
      if (evt == XMLStreamConstants.START_ELEMENT) {
        QName qName = reader.getName();
        if(qName != null){
            if(qName.getPrefix() != null && qName.getPrefix().compareTo("")!=0)
                namespaces.add(String.format("%s, %s, %s",
                    qName.getLocalPart(), qName.getPrefix(), qName.getNamespaceURI()));
        }
      }
}

for(String namespace : namespaces){
    System.out.println(namespace);              
}

答案 1 :(得分:4)

这可以使用单个XPath 2.0表达式完成:

distinct-values(//*[name()!=local-name()]/
   concat(local-name(), ', ', substring-before(name(), ':'), ', ', namespace-uri())

答案 2 :(得分:4)

进一步开发了Michael Kay 提出的XPath表达式(似乎实际上是一种简化),以处理属于默认命名空间的未加前缀的元素名称:

distinct-values(//*[namespace-uri()]
                    /concat(local-name(),
                            ', ',
                            substring-before(name(), ':'),
                            ', ',
                            namespace-uri(),
                            '&#xA;'
                            )
                )

在以下文档中评估此XPath表达式(提供的那个,但在默认命名空间中添加了一个元素):

<ns1:create xmlns:ns1="http://predic8.com/wsdl/material/ArticleService/1/">
    <ns1:article xmlns:ns1="xmlns:ns1='http://predic8.com/material/1/">
        <ns1:id>1</ns1:id>
        <description>bar</description>
        <name>foo</name>
        <ns1:price>
            <amount>00.00</amount>
            <currency>USD</currency>
        </ns1:price>
        <ns1:price>
            <amount>11.11</amount>
            <currency>AUD</currency>
        </ns1:price>
    </ns1:article>
    <ns1:article xmlns:ns1="xmlns:ns1='http://predic8.com/material/1/">
        <ns1:id>2</ns1:id>
        <description>some name</description>
        <name>some description</name>
        <ns1:price>
            <amount>00.01</amount>
            <currency>USD</currency>
        </ns1:price>
        <quality xmlns="my:q">high</quality>
    </ns1:article>
</ns1:create>

产生了想要的正确结果

 create, ns1, http://predic8.com/wsdl/material/ArticleService/1/
 article, ns1, xmlns:ns1='http://predic8.com/material/1/
 id, ns1, xmlns:ns1='http://predic8.com/material/1/
 price, ns1, xmlns:ns1='http://predic8.com/material/1/
 quality, , my:q

进一步的轻微改进也是为属性名称生成命名空间数据:

distinct-values(//(*|@*)[namespace-uri()]
                    /concat(if(. intersect ../@*)
                              then '@'
                              else (),
                            local-name(),
                            ', ',
                            substring-before(name(), ':'),
                            ', ',
                            namespace-uri(),
                            '&#xA;'
                            )
                )

在以下XML文档上评估此XPath表达式(上一个(上面)并在其中一个xml:lang元素上添加了article属性):< / p>

<ns1:create xmlns:ns1="http://predic8.com/wsdl/material/ArticleService/1/">
    <ns1:article xml:lang="en-us" xmlns:ns1="xmlns:ns1='http://predic8.com/material/1/">
        <ns1:id>1</ns1:id>
        <description>bar</description>
        <name>foo</name>
        <ns1:price>
            <amount>00.00</amount>
            <currency>USD</currency>
        </ns1:price>
        <ns1:price>
            <amount>11.11</amount>
            <currency>AUD</currency>
        </ns1:price>
    </ns1:article>
    <ns1:article xmlns:ns1="xmlns:ns1='http://predic8.com/material/1/">
        <ns1:id>2</ns1:id>
        <description>some name</description>
        <name>some description</name>
        <ns1:price>
            <amount>00.01</amount>
            <currency>USD</currency>
        </ns1:price>
        <quality xmlns="my:q">high</quality>
    </ns1:article>
</ns1:create>

再次产生正确的结果:

 create, ns1, http://predic8.com/wsdl/material/ArticleService/1/
 article, ns1, xmlns:ns1='http://predic8.com/material/1/
 @lang, xml, http://www.w3.org/XML/1998/namespace
 id, ns1, xmlns:ns1='http://predic8.com/material/1/
 price, ns1, xmlns:ns1='http://predic8.com/material/1/
 quality, , my:q