2012-06-11 34 views
2

我將一些數據存儲爲XML,並將用戶的更改作爲diff存儲到原始XML中,因此可以在運行時修補用戶的數據。尋找C#中的高性能XyDiff端口(帶有XID的XML差異補丁)

實施例的原始XML(僅其中的一部分):

用戶的diff
<module id=""> 
    <title id=""> 
    ... 
    </title> 
    <actions> 
    ... 
    </actions> 
    <zones id="" selected="right"> 
    <zone id="" key="right" name="Right" /> 
    </zones> 
</module> 

實施例(用戶更改的從右到左選定的值):

<xd:xmldiff version="1.0" srcDocHash="" 
    options="IgnoreChildOrder IgnoreNamespaces IgnorePrefixes IgnoreSrcValidation " 
    fragments="no" 
    xmlns:xd="http://schemas.microsoft.com/xmltools/2002/xmldiff"> 
    <xd:node match="1"> 
    <xd:node match="3"> 
     <xd:change match="@selected">left</xd:change> 
    </xd:node> 
    </xd:node> 
</xd:xmldiff> 

的問題在於,該修補程序查找XML節點的順序。如果訂單發生變化,則差額不能再使用,甚至更差,則會錯誤應用。所以我寧願修補XID。 有沒有人知道一個XyDiff C#的高性能庫或算法?

+0

讓我直截了當地看看:您目前正在使用XyDiff比較兩個XML文件,它會生成顯示的輸出。您對此輸出不滿意,因爲它依賴於有序的XML結構。您現在正在尋找一種diff工具或算法,它可以提供已更改節點的ID以及新值?目前爲止是否正確? – froeschli

+0

是的,正確的。該匹配將顯示Xml節點的Guid。 ... –

+0

但是,我們使用的是XmlDiffPatch的時刻。 –

回答

2

我們現在開發了我們自己的解決方案,工作正常。

我們做了什麼:

  • 確保在原文件中的每個XML節點都有一個唯一的ID(不 無論哪個級別)
  • 生成用於不僅節省了用戶的變化平坦的XML補丁每個變化節點的 變化(無級結構)
  • 如果用戶改變一個值,寫在補丁XML 與屬性的xmlpatch節點目標ID指向原始節點的ID
  • 如果用戶改變的屬性,寫入與新 值到xmlpatch節點
  • 如果用戶改變一個值的屬性,該值寫入到xmlpatch節點

的XML補丁看起來如下:

<patch> 
    <xmlpatch sortorder="10" visible="true" targetId="{Guid-x}" /> 
    <xmlpatch selected="left" targetId="{Guid-y}" /> 
    <xmlpatch targetId="{Guid-z}">true</xmlpatch> 
</patch> 

生成補丁xml的代碼非常簡單。我們通過所有屬性循環遍歷所有xml節點和每個節點。如果節點的屬性或值與原始節點不同,我們將生成帶有屬性或值的修補節點。請注意,代碼寫在一個晚上;)

public static XDocument GenerateDiffGram(XDocument allUserDocument, XDocument runtimeDocument) 
{ 
    XDocument diffDocument = new XDocument(); 
    XElement root = new XElement("patch"); 

    AddElements(root, runtimeDocument, allUserDocument.Root); 

    diffDocument.Add(root); 
    return diffDocument; 
} 

private static void AddElements(XElement rootPatch, XDocument runtimeDocument, XElement allUserElement) 
{ 
    XElement patchElem = null; 
    if (allUserElement.Attribute("id") != null 
     && !string.IsNullOrWhiteSpace(allUserElement.Attribute("id").Value)) 
    { 
     // find runtime element by id 
     XElement runtimeElement = (from e in runtimeDocument.Descendants(allUserElement.Name) 
         where e.Attribute("id") != null 
         && e.Attribute("id").Value.Equals(allUserElement.Attribute("id").Value) 
         select e).FirstOrDefault(); 
     // create new patch node 
     patchElem = new XElement("xmlpatch"); 

     // check for changed attributes 
     foreach (var allUserAttribute in allUserElement.Attributes()) 
     { 
      XAttribute runtimeAttribute = runtimeElement.Attribute(allUserAttribute.Name); 
      if (!allUserAttribute.Value.Equals(runtimeAttribute.Value)) 
      { 
       patchElem.SetAttributeValue(allUserAttribute.Name, runtimeAttribute.Value); 
      } 
     } 

     // check for changed value 
     if (!allUserElement.HasElements 
     && !allUserElement.Value.Equals(runtimeElement.Value)) 
     { 
      patchElem.Value = runtimeElement.Value; 
     } 
    } 

    // loop through all children to find changed values 
    foreach (var childElement in allUserElement.Elements()) 
    { 
     AddElements(rootPatch, runtimeDocument, childElement); 
    } 

    // add node for changed value 
    if (patchElem != null 
     && (patchElem.HasAttributes 
     || !string.IsNullOrEmpty(patchElem.Value))) 
    { 
     patchElem.SetAttributeValue("targetId", allUserElement.Attribute("id").Value); 
     rootPatch.AddFirst(patchElem); 
    } 
} 

在運行時,我們打補丁保存在XML補丁後面的變化。我們通過targettid創建原始節點並覆蓋屬性和值。

public static XDocument Patch(XDocument runtimeDocument, XDocument userDocument, string modulePath, string userName) 
{ 
    XDocument patchDocument = new XDocument(userDocument); 

    foreach (XElement element in patchDocument.Element("patch").Elements()) 
    { 
     // get id of the element 
     string idAttribute = element.Attribute("targetId").Value; 
     // get element with id from allUserDocument 
     XElement sharedElement = (from e in runtimeDocument.Descendants() 
         where e.Attribute("id") != null 
         && e.Attribute("id").Value.Equals(idAttribute) 
         select e).FirstOrDefault(); 

     // element doesn't exist anymore. Maybe the admin has deleted the element 
     if (sharedElement == null) 
     { 
      // delete the element from the user patch 
      element.Remove(); 
     } 
     else 
     { 
      // set attributes to user values 
      foreach (XAttribute attribute in element.Attributes()) 
      { 
       if (!attribute.Name.LocalName.Equals("targetId")) 
       { 
        sharedElement.SetAttributeValue(attribute.Name, attribute.Value); 
       } 
      } 

      // set element value 
      if (!string.IsNullOrEmpty(element.Value)) 
      { 
       sharedElement.Value = element.Value; 
      } 
     } 
    } 

    // user patch has changed (nodes deleted by the admin) 
    if (!patchDocument.ToString().Equals(userDocument.ToString())) 
    { 
     // save back the changed user patch 
     using (PersonalizationProvider provider = new PersonalizationProvider()) 
     { 
      provider.SaveUserPersonalization(modulePath, userName, patchDocument); 
     } 
    } 

    return runtimeDocument; 
}