【问题标题】:Problems parsing XML in Java在 Java 中解析 XML 的问题
【发布时间】:2013-04-20 08:20:12
【问题描述】:

我在解析 XML 文档时遇到了一些麻烦。出于某种原因,有些文本节点是我不希望出现的,因此我的测试变成了红色。 XML 文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<RootNode>
  <PR1>PR1</PR1>
  <ROL>one</ROL>
  <ROL>two</ROL>
  <DG1>DG1</DG1>
  <ROL>three</ROL>
  <ZBK>ZBK</ZBK>
  <ROL>four</ROL>
</RootNode>

现在我有了这个可以重现错误的 sn-p 代码:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(TestHL7Helper.class.getResourceAsStream("TestHL7HelperInput.xml"));
Node root = doc.getFirstChild();
Node pr1 = root.getFirstChild();

检查根变量会产生[RootNode: null],这似乎是正确的,但后来不知何故全错了。 pr1 变量原来是一个文本节点 [#text:\n ] - 但是为什么解析器认为新行和空格是一个文本节点?这不应该被忽视吗?我尝试更改编码,但这也无济于事。有什么想法吗?

如果我删除所有新行和空格并将我的 XML 文档放在一行中,一切正常...

【问题讨论】:

  • 这里有一个 [Dom 解析示例][1] 也许它会对你有所帮助。 [1]:stackoverflow.com/a/7902162/529543
  • 也许应该更改问题标题。我翻阅它以获取有关混合内容和 DOM 解析的一些信息并在那里得到答案,但问题的标题乍一看并没有吸引我。类似于“使用 Java DOM 解析具有混合内容的 XML 时出现问题”。

标签: java xml xml-parsing document


【解决方案1】:

XML 支持混合内容含义元素可以同时具有文本和元素子节点。这是为了支持以下用例:

<text>I've bolded the <b>important</b> part.</text>

input.xml

这意味着默认情况下,DOM 解析器会将以下文档中的空白节点视为重要(以下是您的 XML 文档的简化版本):

<RootNode>
  <PR1>PR1</PR1>
</RootNode>

演示代码

如果您有 XML 架构,则可以在 DocumentBuilderFactory 上设置 ignoringElementContentWhitespace 属性,因为那时 DOM 解析器将知道空格是否以及何时重要。

import java.io.File;
import javax.xml.XMLConstants;
import javax.xml.parsers.*;
import javax.xml.validation.*;

import org.w3c.dom.Document;

public class Demo {

    public static void main(String[] args) throws Exception {
        SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Schema s = sf.newSchema(new File("src/forum16231687/schema.xsd"));

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setSchema(s);
        dbf.setIgnoringElementContentWhitespace(true);

        DocumentBuilder db = dbf.newDocumentBuilder();
        Document d = db.parse(new File("src/forum16231687/input.xml"));
        System.out.println(d.getDocumentElement().getChildNodes().getLength());
    }

}

schema.xsd

如果您创建如下所示的schema.xsd,则演示代码将报告根元素有 1 个子节点。

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
    <element name="RootNode">
        <complexType>
            <sequence>
                <element name="PR1" type="string"/>
            </sequence>
        </complexType>
    </element>
</schema>

如果您更改 schema.xsd 以使 RootNode 具有混合内容,则演示代码将报告 RootNode 具有 3 个子节点。

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
    <element name="RootNode">
        <complexType mixed="true">
            <sequence>
                <element name="PR1" type="string"/>
            </sequence>
        </complexType>
    </element>
</schema>

【讨论】:

    【解决方案2】:

    实际上,其他节点之间的所有文本本身就形成了一个文本节点。因此,如果您使用getFirstChild(),您还将检索这些文本节点。

    在您的情况下,所有非文本子节点都有一个唯一的名称,因此您可以使用 getElementsByTagName() 单独获取它们:

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = factory.newDocumentBuilder();
    Document doc = builder.parse(TestHL7Helper.class.getResourceAsStream("TestHL7HelperInput.xml"));
    Node root = doc.getFirstChild();
    Node pr1 = (root.getElementsByTagName( "PR1" ))[0];
    

    一般来说,我不会依赖于 XML 文档中的位置,而是依赖于标记名称、属性或 ID 等内容。

    【讨论】:

    • 我需要依赖这个,因为我必须在其中一个测试中检查兄弟姐妹。例如,我使用 getElementsByTagName 抓取 PR1 节点,然后我想检查后面有多少 ROL 节点。对于这个任务,我需要使用 getNextSibling() 似乎有完全相同的问题。
    • @Viciouss 也许这个问题会对你有所帮助:stackoverflow.com/q/978810/1169798
    • 我想我会反过来做。我将使用 xpath 来选择与某个名称匹配的兄弟姐妹。其他方法似乎并不令人满意。
    【解决方案3】:

    您可以通过检查节点的类型来解决这个一般问题:

    if (someNode instanceof Element) {
      // ...
    }
    

    这很容易形成循环的一部分,例如:

    NodeList childNodes = root.getChildNodes();
    for (int i = 0; i < childNodes.getLength(); i++) {
      if (childNodes.item(i).getNodeType() == Node.ELEMENT) {
        Element childElement = (Element) childNodes.item(i);
        // ...
      }
    }
    

    或者,使用 XMLBeans 之类的东西来减少手动解析 XML 时引入错误的可能性。获取一个经过良好测试的库来为您完成这项工作!

    【讨论】:

    • 这是我想过的解决方案,但它有点难看。必须有一些东西不会用所有这些 if 和 else 填充我的代码,只是为了检查解析是否有问题。
    • @Viciouss solution from Sirko 更直接,如果你有唯一的名字。或者,您可以考虑使用 XMLBeans 之类的东西来生成专门用于读取与 XSD/DTD 匹配的 XML 文件的类。
    • 我同意@Duncan - 使用现有库为您处理解析。我过去使用过 JDOM,发现它简单直观,学习曲线并不多
    • 我建议使用someNode.getNodeType() == Node.ELEMENT 而不是someNode instanceof Element。我见过 DOM 实现,其中底层 Node impl 类实现了多个接口(即 DocumentElement),instance of 检查可能返回误报。
    • @BlaiseDoughan 很高兴知道,谢谢。事实上,找到了一个关于这个问题的不错的链接:kingsfleet.blogspot.co.uk/2008/10/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-23
    • 1970-01-01
    • 1970-01-01
    • 2012-03-14
    • 1970-01-01
    • 2015-03-27
    • 1970-01-01
    相关资源
    最近更新 更多