C#XML Diffing算法

时间:2015-03-02 21:57:20

标签: c# xml diff

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

有人可以向我建议一个比较好的算法吗?

PS: 我的XML有一个非常简单的模式,它们只是以天真的方式表示对象的结构(带有嵌套对象)。 允许的标签很少,< object>标签只能包含< name>标签,< type>标签或< list>标签。 < name>和< type> tag只能包含一个字符串; <列表>而代码可以包含< name>标签和单个<对象>标签(表示列表中对象的结构)。 < name>中的字符串标签可以自由选择,< type>中的字符串而tag只能是“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>

2 个答案:

答案 0 :(得分:1)

你说:

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

你能更准确地了解你的意思吗?

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

所以,大概你想要计算只改变根元素的改变。如何在您显示的列表中添加新项目?您希望列表计为更改吗?或者如果列表中的对象或列表本身被移动到新的位置而根本没有更改其内容会怎样?

这些可能性中的每一种都非常容易编写,但是必须首先决定什么才算是变化。

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

答案 1 :(得分:0)

  

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

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

下面的代码并不关心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;
                        }
                    }
                }