java - 手动创建所有 Document 节点的 NodeList

标签 java xml dom xpath canonicalization

我目前手动生成所有文档节点(按文档顺序)的 NodeList。获取此 NodeList 的 XPath 表达式是

//. | //@* | //namespace::*

我第一次尝试手动遍历 DOM 并收集节点(NodeSet 是委托(delegate)给 List 的原始 NodeList 实现):

private static void walkRecursive(Node cur, NodeSet nodes) {
    nodes.add(cur);

    if (cur.hasAttributes()) {
        NamedNodeMap attrs = cur.getAttributes();
        for (int i=0; i < attrs.getLength(); i++) {
            Node child = attrs.item(i);
            walkRecursive(child, nodes);
        }
    }

    int type = cur.getNodeType();
    if (type == Node.ELEMENT_NODE || type == Node.DOCUMENT_NODE) {
        NodeList children = cur.getChildNodes();
        if (children == null)
            return;

        for (int i=0; i < children.getLength(); i++) {
            Node child = children.item(i);
            walkRecursive(child, list);
        }
    }
}

我会通过调用 walkRecursive(doc, nodes) 开始递归,其中 docorg.w3c.Document nodes 一个(还为空)NodeSet

我使用原始 XML 文档对此进行了测试:

<?xml version="1.0"?>
<myns:root xmlns:myns="http://www.my.ns/#">
  <myns:element/>
</myns:root>

例如,如果我规范化我手动创建的 NodeSet 和由最初提到的 XPath 表达式生成的 NodeList 并逐字节比较这两个字节,则结果相等并且似乎工作得很好。

但是,如果我遍历两个 NodeList 并打印调试信息(typeString 只是生成一个字符串表示形式)

for (int i=0; i < nodes.getLength(); i++) {
    Node child = nodes.item(i);
    System.out.println("Type: " + typeString(child.getNodeType()) +
                       " Name:" + child.getNodeName() + 
                       " Local name: " + child.getLocalName() +
                       " NS: " + child.getNamespaceURI());
}

然后我收到 XPath 生成的 NodeList 的输出:

Type: DocumentNode Name:#document Local name: null NS: null
Type: Element Name:myns:root Local name: root NS: http://www.my.ns/#
Type: Attribute Name:xmlns:myns Local name: myns NS: http://www.w3.org/2000/xmlns/
Type: Attribute Name:xmlns:xml Local name: xml NS: http://www.w3.org/2000/xmlns/
Type: Text Name:#text Local name: null NS: null
Type: Element Name:myns:element Local name: element NS: http://www.my.ns/#
Type: Text Name:#text Local name: null NS: null

对于手动生成的 NodeList:

Type: DocumentNode Name:#document Local name: null NS: null
Type: Element Name:myns:root Local name: root NS: http://www.my.ns/#
Type: Attribute Name:xmlns:myns Local name: myns NS: http://www.w3.org/2000/xmlns/
Type: Text Name:#text Local name: null NS: null
Type: Element Name:myns:element Local name: element NS: http://www.my.ns/#
Type: Text Name:#text Local name: null NS: null

因此,如您所见,在第一个示例中,NodeList 还包含 XML 命名空间的 Node:

Type: Attribute Name:xmlns:xml Local name: xml NS: http://www.w3.org/2000/xmlns/

现在我的问题:

a) 如果我解释 xml-names11正确,那么我不需要 xmlns:xml 声明:

The prefix xml is by definition bound to the namespace name http://www.w3.org/XML/1998/namespace. It MAY, but need not, be declared, and MUST NOT be undeclared or bound to any other namespace name. Other prefixes MUST NOT be bound to this namespace name, and it MUST NOT be declared as the default namespace.

我说的对吗? (至少 c)朝那个方向暗示)

b) 但是,为什么 XPath 评估无论如何都要添加它 - 它不应该只包含最初存在的内容而不是自动添加内容吗?

c) 这可能会导致 XML canonicalization 出现问题, 虽然它 shouldn't - 在规范化期间应省略 xml 命名空间的声明。有谁知道(Java)实现会出错吗?


编辑:

这是我用来评估包含“xml”命名空间节点的 XPath 表达式的代码:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setValidating(false);
InputStream in = ...;
try {
    Document doc = dbf.newDocumentBuilder().parse(in);
    XPathFactory fac = XPathFactory.newInstance();
    XPath xp = fac.newXPath();
    XPathExpression exp = xp.compile("//. | //@* | //namespace::*");
    NodeList nodes = (NodeList)exp.evaluate(doc, XPathConstants.NODESET);
} finally {
    in.close();
}

最佳答案

既然你会写

<myns:root xml:space="preserve" xmlns:myns="http://www.my.ns/#">
  <myns:element/>
</myns:root>

如果不声明“xml”前缀,则它必须隐式存在。因此,在 //namespace:* location step

中包含此命名空间声明的命名空间节点是正确的

所以,

a) 你错了,你需要它(好吧,取决于你代码的目的)

b) 见上文

c) 不,但我见过其他命名空间的极端情况,其中事情变得一团糟(例如 Problem with conversion of org.dom4j.Document to org.w3c.dom.Document and XML Signature

关于java - 手动创建所有 Document 节点的 NodeList,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6990624/

相关文章:

java - 如何用不同的值初始化每个线程

java - 带重音符号的 Spring RestTemplate 文件名

java - 重写而不是附加到 XML 文件

javascript - 在javascript中重新排序节点列表

javascript - 无法使用innerhtml或选择节点,返回null

java - 如何修改xml文档并将其作为java中的字符串返回?

java - 从列表对象中删除一个元素

java - 编辑 JTable 中新添加的行

java:写入xml文件

xml - 从 scala 的 XML 到 w3c DOM 的任何转换?