Java中DOM解析中的规范化 - 它是如何工作的?

255

我在这个教程中看到以下代码,用于DOM解析器。

doc.getDocumentElement().normalize();

我们为什么要进行归一化处理?
我看了文档,但一个字都没看懂。

将此节点下方子树的所有Text节点合并到一起。

好的,那么有人能展示给我(最好附带图片)这棵树的样子吗?

有人可以解释一下为什么需要归一化吗?
如果不进行归一化会发生什么?


3
@wulfgar.pro - 我理解你所说的。但是,我想要理解我在问题中提出的内容。我也很快会进行SAX解析。 - Apple Grinder
在谷歌上搜索“规范化XML”会得到一些看起来有用的结果。它似乎类似于数据库中的规范化。 - Apple Grinder
如果你只读每个句子的前三分之一,你永远不会理解它。尝试阅读你引用的整个句子。意思清晰明了。 - user207421
2
@EJP - 呃...还是不太清楚,因为我对XML并不了解,只是读了几页入门内容。顺便说一下,别误会,你和文档作者做的事情是一样的——使用复杂的词汇而不是简单的英语(像一个长棍子一样简单易懂)。对我来说,先用简单的词汇再用行话更有效。 - Apple Grinder
8
截至本篇撰写时,该网站引用了此 Stack Overflow 帖子。我的大脑刚刚抛出了一个依赖错误。 - chessofnerd
显示剩余2条评论
3个回答

385

余下的句子是:

其中只有结构(例如元素、注释、处理指令、CDATA 部分和实体引用)分隔文本节点,即既没有相邻的文本节点也没有空白文本节点。

这基本上意味着以下 XML 元素:

<foo>hello 
wor
ld</foo>

在非规范化节点中,可以表示为这样:

Element foo
    Text node: ""
    Text node: "Hello "
    Text node: "wor"
    Text node: "ld"

当节点被规范化后,它将会看起来像这样

Element foo
    Text node: "Hello world"

同样的规则也适用于属性:<foo bar="Hello world"/>,注释等。


2
啊哈!现在更清楚了。我不知道数据结构和节点是什么。但我快速查看了树形结构,猜测计算机可能会按照你建议的方式存储“hello world”。是这样吗? - Apple Grinder
9
你需要学习DOM的基础知识。是的,DOM将XML文档表示为一棵树。在树中,你有一个根节点,它有子节点,每个子节点也有子节点,以此类推。这就是树的结构。元素是一种节点,文本节点是另一种节点。 - JB Nizet
7
感谢JB Nizet。得到了一些指引后,我感到非常宽慰。 - Apple Grinder
2
@user2043553,这里实际上是在强调换行符的重要性。如果没有换行符,您将看不到差异。如果您不进行规范化,则可能会发生这样的情况,即同一类型(或在同一个标记中)的多个元素之间被解释为分隔符的新行。 - Stacky
1
@Stacky,在这个例子中有两个新行,它们在规范化后没有显示出来,这可能会让人们认为它们不再存在。 带有换行符的结果文本节点将如下所示:“Hello\nwor\nld” 规范化不会删除换行符。 - Christian
显示剩余4条评论

12

简而言之,规范化是减少冗余的过程。
冗余示例:
a) 根/文档标签外的空格(...<document></document>...
b) 开始标签(<...>)和结束标签(</...>)内的空格
c) 属性及其值之间的空格(即在键名称=“之间的空格)
d) 多余的命名空间声明
e) 属性和标记文本中的换行符/空格等
f) 注释等...


7
作为对 @JBNizet 的答案的扩展,针对更加技术性的用户,下面是 com.sun.org.apache.xerces.internal.dom.ParentNode 实现 org.w3c.dom.Node 接口的样式,可以让你了解它实际上是如何工作的。
public void normalize() {
    // No need to normalize if already normalized.
    if (isNormalized()) {
        return;
    }
    if (needsSyncChildren()) {
        synchronizeChildren();
    }
    ChildNode kid;
    for (kid = firstChild; kid != null; kid = kid.nextSibling) {
         kid.normalize();
    }
    isNormalized(true);
}

它递归遍历所有节点并调用kid.normalize()函数。
org.apache.xerces.dom.ElementImpl中,此机制被覆盖。

public void normalize() {
     // No need to normalize if already normalized.
     if (isNormalized()) {
         return;
     }
     if (needsSyncChildren()) {
         synchronizeChildren();
     }
     ChildNode kid, next;
     for (kid = firstChild; kid != null; kid = next) {
         next = kid.nextSibling;

         // If kid is a text node, we need to check for one of two
         // conditions:
         //   1) There is an adjacent text node
         //   2) There is no adjacent text node, but kid is
         //      an empty text node.
         if ( kid.getNodeType() == Node.TEXT_NODE )
         {
             // If an adjacent text node, merge it with kid
             if ( next!=null && next.getNodeType() == Node.TEXT_NODE )
             {
                 ((Text)kid).appendData(next.getNodeValue());
                 removeChild( next );
                 next = kid; // Don't advance; there might be another.
             }
             else
             {
                 // If kid is empty, remove it
                 if ( kid.getNodeValue() == null || kid.getNodeValue().length() == 0 ) {
                     removeChild( kid );
                 }
             }
         }

         // Otherwise it might be an Element, which is handled recursively
         else if (kid.getNodeType() == Node.ELEMENT_NODE) {
             kid.normalize();
         }
     }

     // We must also normalize all of the attributes
     if ( attributes!=null )
     {
         for( int i=0; i<attributes.getLength(); ++i )
         {
             Node attr = attributes.item(i);
             attr.normalize();
         }
     }

    // changed() will have occurred when the removeChild() was done,
    // so does not have to be reissued.

     isNormalized(true);
 } 

希望这能为您节省一些时间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接