2015-03-02 3 views
1

У меня есть два XML, до и после того, как пользователь их редактировал. Мне нужно проверить, что пользователь добавил только новые элементы, но не удалил или не изменил старые.C# XML Diffing algorithm

Может ли кто-нибудь предложить мне хороший алгоритм для сравнения?

Пс: Мой XML имеет очень тривиальную схему, они представляют собой структуру объекта (с вложенными объектами) наивным образом. Есть несколько разрешенных тегов, < объекта > тег может только содержит <Name> тега, < типа > тега или < списка > тега. < имя > и < тип > тег может содержать только строку; <list> тег вместо этого может содержать < имя > тег и один объект < объект > теги (представляющие структуру объектов в списке). Строка в значении < > может быть свободно выбрана, строка в < Тип > тегом вместо этого могут быть только «строка», «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

ответ

1

Вы сказали:

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

Можете ли вы быть более точным о том, что вы имеете в виду?

Например, если я где-то вставляю новый элемент «объект», я изменил каждый элемент внутри, правильно? Так много списков и других объектов, которые содержат его. Фактически, любая установка - это изменение корневого элемента.

Итак, предположительно вы хотите, чтобы не подсчеты, которые ничего не меняют, кроме элемента root. Как добавить новый элемент в список, который вы показываете? Вы хотите, чтобы список считался измененным? Или что, если объекты в списке или сам список перемещаются в новые места без изменения их содержимого?

Каждая из этих возможностей довольно проста в написании, но нужно решить, что считается изменением в первую очередь.

Если, например, вас интересуют только объекты нижнего уровня, а «то же самое» означает точно то же текстовое содержимое (без атрибутов, вариантов белого пространства и т. Д.), Тогда самый простой способ - загрузите файл «before» в список пар (имя, тип); затем загрузите файл «после» в аналогичный, но отдельный список. Сортируйте оба списка, затем выполните их одновременное и сообщите что-нибудь в новом, которое не находится в старом (вы, вероятно, захотите также сообщить о каких-либо удалениях, на всякий случай).

+0

Вы правы, я не знал о изменениях. Я имею в виду, что вы не можете изменять имя или тип существующих объектов или удалять их, но вы можете добавить подструктуры (вложенные объекты). Все объекты действительно представляют собой схему структур объекта, поэтому список не содержит элементов, списки содержат только определение схемы единого объекта, на которую привязаны. Итак, для списков применяется то же правило другого объекта, вы можете добавить новую структуру к объекту, на котором привязаны списки, но вы не можете переименовать существующий или удалить старый. – Skary

+0

Извините за двойной комментарий, но дайте мне понять, что вы говорите. Вы предлагаете создать два списка (старый xml новый xml), который содержит иерархию каждого элемента с ассоциированным с ним типом (очень тривиальный пример должен состоять в том, что иерархия является конкатенацией имен предков + имя текущего элемента + некоторое специальное разделительное имя char + type), сортировать оба списка, а затем проверить, есть ли во втором списке все элементы первого списка? На первый взгляд кажется, что это работает, и это довольно просто или я пропустил что-то важное? – Skary

+0

Это будет работать, если не имеет значения, где «объект» встречается в иерархии, или если, как вы упомянули, вы присоединяетесь ко всем типам элементов (например, «object/list/object # composite» или что-то еще). Если порядок имеет значение (скажем, я перемещаю одного брата позже), вам также нужно сохранить дочерние числа: «object_1/list_1/object_3 # composite». Если вы также не заботитесь о вложенности (перемещение объекта по-прежнему считается «одним и тем же объектом», то вы можете полностью сгладить его полностью в один массив объектов. – TextGeek

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