2015-03-02 84 views
1

我有兩個XML,用戶編輯它們之前和之後。我需要檢查用戶是否只添加了新元素,但沒有刪除或更改舊元素。C#XML Diffing算法

有人可以向我建議一個很好的算法來做比較嗎?

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> 
+0

您的用戶如何編輯XML?爲什麼不給他們一個只允許添加新節點的接口? – jac 2015-03-02 23:14:20

回答

1

你說:

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

你能更精確地瞭解你是什麼意思?例如,如果我在某處插入一個新的「對象」元素,我已經更改了其中的每個元素,對吧?包含它的許多列表和其他對象。實際上,任何插入都是對根元素的改變。

所以,大概你想而不是計數改變,只改變根元素。如何將新項目添加到您顯示的列表中?你想列表計數改變?或者如果列表中的對象或列表本身移動到新的位置而根本沒有改變其內容?

這些可能性中的每一個都很容易編寫,但必須首先確定什麼纔是重要的改變。

例如,如果您只關心底層對象,而「相同」意味着完全相同的文本內容(無屬性,空白變化等等),那麼最簡單的方法是將「before」文件加載到(名稱,類型)對列表中;然後將「after」文件加載到類似但分開的列表中。對兩個列表進行排序,然後同時運行它們,並在新的列表中報告任何不在舊列表中的任何內容(爲防萬一,您可能還想報告任何刪除操作)。

+0

你是對的,我不清楚變化。 我的意思是,你不能改變名稱或類型的現有對象或刪除它們,但你可以添加子結構(嵌套對象)。 所有對象的確是對象結構的模式,所以列表不包含元素,列表只包含綁定到的單個對象模式的定義。 因此,對於列表應用其他對象的相同規則的列表,可以將新結構添加到列表綁定到的對象,但不能重命名現有的或刪除舊的結構。 – Skary 2015-03-03 05:28:45

+0

對不起,雙重評論,但讓我明白你說什麼。 您建議創建兩個列表(舊的xml和一個新的xml),其中包含每個元素的層次結構及其關聯的類型(一個非常簡單的示例應該是層次結構是祖先名稱+當前元素名稱+某些特殊分割字符+類型名稱),對兩個列表進行排序,然後檢查第二個列表中是否有第一個列表的所有元素? 乍一看似乎有效,它很簡單,或者我錯過了一些至關重要的東西? – Skary 2015-03-03 05:39:36

+0

如果在層次結構中出現「對象」的位置並不重要,或者如前所述,您加入了所有元素類型(如「object/list/object#composite」或其他),那麼這將起作用。如果順序很重要(比如我稍後移動一個兄弟),那麼你還需要保留子編號:「object_1/list_1/object_3#composite」。如果你根本不關心嵌套(移動一個對象仍然被視爲「同一個對象」,那麼你可以將它完全展平成一個對象數組。 – TextGeek 2015-03-03 19:11:24

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; 
         } 
        } 
       }