2

我在解析 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>

现在我有这段代码可以重现错误:

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 文档放在一行中,那么一切正常......

4

3 回答 3

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 等内容。

于 2013-04-26T08:19:58.267 回答
2

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

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

输入.xml

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

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

演示代码

如果你有一个 XML 模式,你可以ignoringElementContentWhitespace在 since 上设置属性,DocumentBuilderFactory那么 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());
    }

}

架构.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>
于 2013-04-26T10:55:26.237 回答
0

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

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 时引入错误的可能性。获得一个经过良好测试的库来为您完成工作!

于 2013-04-26T08:19:27.957 回答