C# XML Diffing algorithm

本文关键字:algorithm Diffing XML | 更新日期: 2024-09-19 04:17:35

我有两个XML,在用户编辑它们之前和之后。我需要检查用户是否只添加了新元素,但没有删除或更改旧元素。

有人能给我推荐一个好的算法来进行比较吗?

Ps:我的XML有一个非常琐碎的模式,它们只是以一种天真的方式表示对象的结构(带有嵌套对象)。允许的标记很少,<对象>标记只能包含<名称>标签,<类型>标签或<列表>标签<名称>并且<类型>标记只能包含一个字符串<列表>标记可以包含<名称>标签和单个<对象>标记(表示列表中对象的结构)。<名称>标签可以自由选择,<类型>标记只能是"string"、"int"、"float"、"bool"、"date"或"composite"。

这里有一个例子:

 <object>
      <name>Person</name>
      <type>composite</type>
      <object>
            <name>Person_Name</name>
            <type>string</type>
      </object>
      <object>
            <name>Person_Surname</name>
            <type>string</type>
      </object>
      <object>
            <name>Person_Age</name>
            <type>int</type>
      </object>
      <object>
            <name>Person_Weight</name>
            <type>float</type>
      </object>
      <object>
            <name>Person_Address</name>
            <type>string</type>
      </object>
      <object>
            <name>Person_BirthDate</name>
            <type>date</type>
      </object>
      <list>
            <name>Person_PhoneNumbers</name>
            <object>
                  <name>Person_PhoneNumber</name>
                  <type>composite</type>
                  <object>
                        <name>Person_PhoneNumber_ProfileName</name>
                        <type>string</type>
                  </object>
                  <object>
                        <name>Person_PhoneNumber_CellNumber</name>
                        <type>string</type>
                  </object>
                  <object>
                        <name>Person_PhoneNumber_HomeNumber</name>
                        <type>string</type>
                  </object>
                  <object>
                        <name>Person_PhoneNumber_FaxNumber</name>
                        <type>string</type>
                  </object>
                  <object>
                        <name>Person_PhoneNumber_Mail</name>
                        <type>string</type>
                  </object>
                  <object>
                        <name>Person_PhoneNumber_Social</name>
                        <type>string</type>
                  </object>
                  <object>
                        <name>Person_PhoneNumber_IsActive</name>
                        <type>bool</type>
                  </object>
            </object>
      </list>
 </object>

C# XML Diffing algorithm

你说:

I need to check that user have only added new elements 
but have not deleted or changed old ones.

你能更准确地说出你的意思吗?

例如,如果我在某个地方插入一个新的"对象"元素,我已经更改了它内部的每个元素,对吧?尽可能多的列表和其他对象包含它。事实上,任何插入都是对根元素的更改。

因此,大概您想而不是计数只更改根元素的更改。在您显示的列表中添加一个新项目怎么样?是否要将列表计算为已更改?或者,如果列表中的对象或列表本身被移动到新的位置而其内容根本没有更改,该怎么办?

每一种可能性都很容易写出来,但必须首先决定什么才算是改变。

例如,如果您只关心底层对象,而"相同"意味着完全相同的文本内容(没有属性、空白变体等),那么最简单的方法是将"之前"文件加载到(名称、类型)对的列表中;然后将"after"文件加载到一个类似但单独的列表中。对这两个列表进行排序,然后同时运行它们,并报告新列表中不在旧列表中的任何内容(您可能也想报告任何删除内容,以防万一)。

我需要检查用户是否只添加了新元素,但没有删除或更改旧元素。

您可以将2个XML文件表示为对象。遍历节点,获取每个节点的子元素计数,并检查其子节点是否存在于其他文件中。对于比较2个复杂对象,可以使用IEquatable.Equals()接口方法。请在此处阅读。

下面的代码并不关心XML文档的结构,也不关心特定元素存在于哪个位置,因为每个元素都表示为XElement对象。它只知道1.)元素的名称,2.)每个元素是否有子元素,3.)是否有属性,4.)是否有innerxml,等等。如果你想严格控制XML的结构,你可以将每个级别表示为一个单独的类。

public class Program
{
    static void Main(string[] args)
    {
        XDocument xdoc1 = XDocument.Load("file1.xml");
        XDocument xdoc2 = XDocument.Load("file2.xml");
        RootElement file1 = new RootElement(xdoc1.Elements().First());
        RootElement file2 = new RootElement(xdoc2.Elements().First());
        bool isEqual = file1.Equals(file2);
        Console.ReadLine();
    }
}
public abstract class ElementBase<T>
{
    public string Name;
    public List<T> ChildElements;
    public ElementBase(XElement xElement)
    {
    }
}
public class RootElement : ElementBase<ChildElement>, IEquatable<RootElement>
{
    public RootElement(XElement xElement)
        : base(xElement)
    {
        ChildElements = new List<ChildElement>();
        Name = xElement.Name.ToString();
        foreach (XElement e in xElement.Elements())
        {
            ChildElements.Add(new ChildElement(e));
        }
    }
    public bool Equals(RootElement other)
    {
        bool flag = true;
        if (this.ChildElements.Count != other.ChildElements.Count())
        {
            //--Your error handling logic here
            flag = false;
        }
        List<ChildElement> otherChildElements = other.ChildElements;
        foreach (ChildElement c in this.ChildElements)
        {
            ChildElement otherElement = otherChildElements.FirstOrDefault(x => x.Name == c.Name);
            if (otherElement == null)
            {
                //--Your error handling logic here
                flag = false;
            }
            else
            {
                flag = c.Equals(otherElement) == false ? false : flag;
            }
        }
        return flag;
    }
}
public class ChildElement : ElementBase<ChildElement>, IEquatable<ChildElement>
{
    public ChildElement(XElement xElement)
        : base(xElement)
    {
        ChildElements = new List<ChildElement>();
        Name = xElement.Name.ToString();
        foreach (XElement e in xElement.Elements())
        {
            ChildElements.Add(new ChildElement(e));
        }
    }
    public bool Equals(ChildElement other)
    {
        bool flag = true;
        if (this.ChildElements.Count != other.ChildElements.Count())
        {
            //--Your error handling logic here
            flag = false;
        }
        List<ChildElement> otherList = other.ChildElements;
        foreach (ChildElement e in this.ChildElements)
        {
            ChildElement otherElement = otherList.FirstOrDefault(x => x.Name == e.Name);
            if (otherElement == null)
            {
                //--Your error handling logic here
                flag = false;
            }
            else
            {
                flag = e.Equals(otherElement) == false ? false : flag;
            }
        }
        return flag;
    }
}

如果您还想检查属性或innerxml,可以这样做

public List<XAttribute> ElementAttributes = new List<XAttribute>();
    foreach (XAttribute attr in xElement.Attributes())
                {
                    ElementAttributes.Add(attr);
                }
List<XAttribute> otherAttributes = other.ElementAttributes;
                foreach (XAttribute attr in ElementAttributes)
                {
                    XAttribute otherAttribute = otherAttributes.FirstOrDefault(x => x.Name == attr.Name);
                    if (otherAttribute == null)
                    {
                        //--Your error handling logic here
                        flag = false;
                    }
                    else
                    {
                        if (otherAttribute.Value != attr.Value)
                        {
                            //--Your error handling logic here
                            flag = false;
                        }
                    }
                }