public static XNode Build(XmlNode node, XNode parent)
{
if (node.NodeType == XmlNodeType.Attribute)
{
var xnode = new XNode(node, parent);
xnode.Name = node.Name.ToLowerInvariant();
var name = "@" + xnode.Name;
xnode.Hash = Murmur3Hasher.HashString(name + "/" + (node.Value ?? string.Empty));
return xnode;
}
else if (node.NodeType == XmlNodeType.Text || node.NodeType == XmlNodeType.CDATA)
{
var xnode = new XNode(node, parent);
xnode.Name = "#text";
xnode.Hash = Murmur3Hasher.HashString(xnode.Name + "/" + (node.Value ?? string.Empty));
return xnode;
}
else if (node.NodeType == XmlNodeType.Element)
{
var xnode = new XNode(node, parent);
var name = node.Name.ToLowerInvariant();
xnode.Name = name;
var hashes = new List<byte[]>();
hashes.Add(Murmur3Hasher.HashString(name + "/"));
// Add attributes
var attributes = new List<XNode>();
for (var i = 0; i < node.Attributes.Count; i++)
{
var child = XNode.Build(node.Attributes[i], xnode);
if (child != null)
{
hashes.Add(child.Hash);
attributes.Add(child);
}
}
xnode.Attributes = attributes.ToArray();
// Add child elements and text nodes
var children = new List<XNode>();
var elements = new List<XNode>();
var texts = new List<XNode>();
for (var i = 0; i < node.ChildNodes.Count; i++)
{
var child = XNode.Build(node.ChildNodes[i], xnode);
if (child != null)
{
hashes.Add(child.Hash);
children.Add(child);
if (child.IsElement())
elements.Add(child);
else
texts.Add(child);
}
}
xnode.Children = children.ToArray();
xnode.Elements = elements.ToArray();
xnode.Texts = texts.ToArray();
// Sort and concatenate child hashes and then compute the hash
var joined = ConcatAll(hashes.OrderBy(h => h, ByteArrayComparer.Instance)
.ToList(), Murmur3Hasher.OUTPUT_LENGTH);
xnode.Hash = Murmur3Hasher.HashBytes(joined);
return xnode;
}
return null;
}