C# 一种高效的XML节点比较算法
我想确定XML文档中两个不同的子节点是否相等。如果两个节点具有相同的属性集和子注释,并且所有子注释也相等(即整个子树应相等),则应将其视为相等 输入文档可能非常大(高达60MB,需要比较的节点超过100000个),性能是一个问题 检查两个节点是否相等的有效方法是什么 示例:C# 一种高效的XML节点比较算法,c#,xml,algorithm,comparison,performance,C#,Xml,Algorithm,Comparison,Performance,我想确定XML文档中两个不同的子节点是否相等。如果两个节点具有相同的属性集和子注释,并且所有子注释也相等(即整个子树应相等),则应将其视为相等 输入文档可能非常大(高达60MB,需要比较的节点超过100000个),性能是一个问题 检查两个节点是否相等的有效方法是什么 示例: <w:p> <w:pPr> <w:spacing w:after="120"/> </w:pPr> <w:r> <w:t>H
<w:p>
<w:pPr>
<w:spacing w:after="120"/>
</w:pPr>
<w:r>
<w:t>Hello</w:t>
</w:r>
</w:p>
<w:p>
<w:pPr>
<w:spacing w:after="240"/>
</w:pPr>
<w:r>
<w:t>World</w:t>
</w:r>
</w:p>
你好
世界
此XML片段描述OpenXML文档中的段落。该算法将用于确定文档中是否包含与文档前面的另一个段落具有相同属性(w:pPr节点)的段落(w:p节点)
我的一个想法是将节点的外部XML存储在一个散列集中(通常我必须首先获得一个规范的字符串表示,其中属性和子注释总是以相同的方式排序,但我可以预期我的节点已经是这种形式)
另一个想法是为每个节点创建一个XmlNode对象,并编写一个比较器来比较所有属性和子节点
我的环境是C#(.NET2.0);欢迎任何反馈和进一步的想法。也许有人已经有了一个好的解决方案
编辑:微软的XmlDiff API实际上可以做到这一点,但我想知道是否会有一种更轻量级的方法。XmlDiff似乎总是首先生成diffgram和规范节点表示,这两件事我都不需要
EDIT2:根据这里提出的建议,我终于实现了自己的XMLNodeQualityComparer。非常感谢
谢谢,
divo不是对您的问题的直接回答,而是与您想要实现的目标密切相关:看看(.net XML power tools)这种方法怎么样: 对于文档中的所有
节点(我假设每个
节点不超过一个),将所有相关数据(元素名称、属性、值)连接到一个字符串中:
// string format is really irrelevant, so this is just a bogus example
'!w:keep-with-next@value="true"!w:spacing@w:before="10"@w:after="120"'
按字母顺序执行,以说明不同的文档顺序
使用这些字符串作为键,使用对相应
节点的引用作为值,构建一个集合
在执行此操作的过程中,当您发现集合中已经存在给定的键时,您会发现一个具有相同属性的段落。如果要继续收集,请使用节点列表作为收集值
我不能说它的性能有多好,但我想实现和发现它并不难。我建议不要使用自己的散列创建函数,而是使用内置的
XNodeEqualityComparer
的GetHashCode
方法。这保证了在创建结果时考虑属性和子代节点,也可以节省一些时间
您的代码如下所示:
XNodeEqualityComparer comparer = new XNodeEqualityComparer();
XDocument doc = XDocument.Load("XmlFile1.xml");
Dictionary<int, XNode> nodeDictionary = new Dictionary<int, XNode>();
foreach (XNode node in doc.Elements("doc").Elements("node"))
{
int hash = comparer.GetHashCode(node);
if (nodeDictionary.ContainsKey(hash))
{
// A duplicate has been found. Execute your logic here
// ...
}
else
{
nodeDictionary.Add(hash, node);
}
}
XNodeEqualityComparer comparer=新的XNodeEqualityComparer();
XDocument doc=XDocument.Load(“XmlFile1.xml”);
字典nodeDictionary=新字典();
foreach(文档元素(“文档”).Elements(“节点”)中的XNode节点)
{
int hash=comparer.GetHashCode(节点);
if(nodeDictionary.ContainsKey(散列))
{
//已找到重复项。请在此处执行您的逻辑
// ...
}
其他的
{
添加(散列,节点);
}
}
我的XmlFile1.xml是:
<?xml version="1.0" encoding="utf-8" ?>
<doc>
<node att="A">Blah</node>
<node att="A">Blah</node>
<node att="B">
<inner>Innertext</inner>
</node>
<node>Blah</node>
<node att="B">
<inner>Different</inner>
</node>
</doc>
废话
废话
内部文本
废话
不同的
nodeDictionary
最终将包含唯一的节点及其散列集合。使用字典
的ContainsKey
方法检测重复项,传入节点的散列,我们使用XNodeEqualityComparer
的GetHashCode
方法生成散列
我认为这应该足够快以满足您的需要。即使正确定义 “当两个xml文档相等时?” 原因有很多:
我的建议是将该函数与兼容的XPath 2.0引擎一起使用。这里是我设计的一个哈希函数,它试图解决部分问题。请注意,我很少有编写哈希函数的经验,并且主要是为了从人们那里获得关于它在解决这个特定问题中的有效性的反馈。我不建议在生产中使用它
static int HashXElement(XElement elem)
{
int hash = 23;
foreach (XAttribute attrib in elem.Attributes())
{
int attribHash = 23;
attribHash = attribHash * 37 + attrib.Name.GetHashCode();
attribHash = attribHash * 37 + attrib.Value.GetHashCode();
hash = hash ^ attribHash;
}
foreach(XElement subElem in elem.Descendants())
{
hash = hash * 37 + XmlHash(subElem);
}
hash = hash * 37 + elem.Value.GetHashCode();
return hash;
}
想法是使子节点的顺序变得重要,但属性的顺序并不重要。相关帖子:嗨,谢谢你的评论。XmlDiff似乎很好,但对于我的具体问题来说,它似乎相当沉重。我不需要找到任何关于差异的信息,一个简单的等于或不等于测试就足够了,我也不需要创建一个规范化的表示形式,该表示形式使用XmlDiff