首页 文章

手动创建所有Document节点的NodeList

提问于
浏览
4

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

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

我第一次尝试手动遍历DOM并收集节点( NodeSet 是委托给 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.Documentnodes a(还是空的) NodeSet .

我使用这个原始XML文档测试了这个:

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

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

But ,如果我遍历两个 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声明:

根据定义,前缀xml绑定到命名空间名称http://www.w3.org/XML/1998/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();
}

1 回答

  • 1

    既然你可以写

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

    在没有声明"xml"前缀的情况下,它必须是隐含的 . 因此,在 //namespace:* 位置步骤中包含此命名空间声明的命名空间节点是正确的

    所以,

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

    b)见上文

    c)没有,但我已经看到其他名称空间角落的情况,事情变得混乱(例如Problem with conversion of org.dom4j.Document to org.w3c.dom.Document and XML Signature

相关问题