2013-10-19 54 views
4

這裏有一個棘手的問題。比較兩個xmlfiles,添加缺少的元素

我得到了一個文件,MainFile.XML看起來像這樣:

<xml> 
    <header> 
    <some></some> 
    <addThis></addThis> 
    </header> 
    <footer></footer> 
    <this> 
    <is> 
     <deep> 
     <like></like> 
    </deep> 
    </is> 
    </this> 
<test></test> 
<page></page> 
<addThis></addThis> 

而我的其他文件,LangFile.XML看起來是這樣的。

<xml> 
    <header> 
    <some>English file</some> 
    </header> 
    <footer>Footer</footer> 
    <this> 
    <is> 
     <deep> 
     <like>Hey</like> 
     </deep> 
    </is> 
    </this> 
    <test>Something</test> 
</xml> 

我想,這樣是我MainFile.XML匹配來更新我的LangFile.XML但我需要把所有的文本值在LangFile。

我想LangFile看起來像這樣的更新之後: 預期產出

<xml> 
    <header> 
    <some>English file</some> 
    <addThis></addThis> 
    </header> 
    <footer>Footer</footer> 
    <this> 
    <is> 
    <deep> 
     <like>Hey</like> 
    </deep> 
    </is> 
    </this> 
    <test>Something</test> 
    <page></page> 
    <addThis></addThis> 
</xml> 

我看了這個答案,但我需要更新文件,並保留值... Compare two text files line by line

棘手的部分是嵌套,也可以是在1級到X級深什麼...

我的問題是,I D ont知道如何逐行比較行中的樹時,我嘗試了這樣的東西,但我堅持...我不知道如何將特定的後代添加到新的列表。

String directory = @"C:\Utv\XmlTest"; 

var mainFile = XDocument.Load(Path.Combine(directory, "MainFile.XML")); 
var langFile = XDocument.Load(Path.Combine(directory, "LangFile.XML")); 

//Get all descendant nodes 
var mainFileDesc = mainFile.Root.Descendants().ToList(); 
var langFileDesc = langFile.Root.Descendants().ToList(); 

//Loop through the mainfile 
for(var i = 0; i < mainFileDesc.Count(); i++) 
{ 
    var mainRow = mainFileDesc[i]; 
    var langRow = langFileDesc[i]; 

    //Compare the rows descendants, if not the same, add the mainRow to the langRow 
    if(mainRow.Descendants().Count() != langRow.Descendants().Count()) 
    { 
     //Here I want to check if the mainRow != the langRow 
        //if not, add the mainRow to the langFile list 
        if(mainRow != langRow) 
        { 
         langFileDesc.Insert(i, mainRow); 
        } 
    } 
} 

即時得到下面的錯誤現在:

var langRow = langFileDesc[i]; 
Message Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index 

這是因爲清單不具有相同的長度,這就是爲什麼我需要將其添加到列表...

+0

可以有多少物品?多少嵌套?任何ID值?如果當兩個文本值不一致? –

+0

mainfile永遠不會有任何文本值。嵌套是靈活的,它可以是1 -1000之間的任何水平... – JOSEFtw

+0

1000個關卡?你不應該在那裏使用XML,你可能在這裏看一個遞歸的解決方案。 –

回答

2

據我瞭解,你想要做的是用另一個xml文件更新一個xml文件,考慮到這兩個文件具有相似的結構。如果是的話,你可以嘗試用XPath來做到這一點,這將是這樣的:

private static void Main(string[] args) 
    { 
     try 
     { 
      XmlDocument xml1 = new XmlDocument(); 
      xml1.Load(@"C:\testxml\MainFile.xml"); 
      XPathNavigator nav = xml1.CreateNavigator(); 

      //Create the langFile Navigator 
      XPathDocument xml2 = new XPathDocument(@"C:\testxml\LangFile.xml"); 
      XPathNavigator nav2 = xml2.CreateNavigator(); 
      //Select all text nodes. 
      var nodes = nav2.SelectDescendants(XPathNodeType.Text, true); 
      while (nodes.MoveNext()) 
      { 
       //Update the MainFile with the values in the LangFile 

       var c = nav.SelectSingleNode(GetPath(nodes.Clone().Current));//(1*) 
       if(c != null) 
       { 
        c.MoveToFirstChild(); 
        c.SetValue(nodes.Current.Value); 
       } 
      } 
      Console.WriteLine(xml1.InnerXml); 
      Console.ReadKey(); 
     } 
     catch 
     { 
     } 
    } 
    private static string GetPath(XPathNavigator navigator) 
    { 
     string aux =string.Empty; 
     while (navigator.MoveToParent()) 
     { 
      aux = navigator.Name + "/"+aux; 
     } 
     return "/" + (aux.EndsWith("/")?aux.Remove(aux.LastIndexOf('/')):aux); 
    } 

有了這個,你不需要知道多少嵌套有你的文件,但如果XML有一個以上的節點與(* 1)評論相同的名稱,您應該管理它。 我希望這可以幫助。

+0

是的我想要做以下事情:有一個MainFile.XML,在這個文件中,我將能夠手動添加行。然後我想比較我的LangFile.XML和MainFile,如果mainfile中的行不存在於Langfile中,我想添加它。我有更多時間看看你的代碼,但看起來不錯! – JOSEFtw

+0

工程就像一個魅力!謝謝 – JOSEFtw

+1

不客氣! :) –

2

您可能想要遞歸執行此操作。我假設一個簡單的文件副本不是你想要的。

猜測寫這篇文章時,你檢查了另一個答案。我希望它適合你,但這是另一種方法。我的方法看起來更復雜,所以如果馬里亞諾對你的作品很棒。

/// <summary> 
/// Copy A to B where B doesn't have A nodes. 
/// </summary> 
public static void EvenUp(XElement A, XElement B) 
{ 
    XNode lastB = null, nodeA = null, nodeB = null; 

    Action Copy_A_To_B =() => 
    { 
     if (null == lastB) 
      B.AddFirst(nodeA); 
     else 
      lastB.AddAfterSelf(nodeA); 
    }; 

    var listA = A.Nodes().ToList(); 
    var listB = B.Nodes().ToList(); 
    int a, b; 

    for (a = 0, b = 0; a < listA.Count && b < listB.Count; a++, b++) 
    { 
     nodeA = listA[a]; 
     nodeB = listB[b]; 

     XElement xA = nodeA as XElement, 
      xB = nodeB as XElement; 

     XText tA = nodeA as XText, 
      tB = nodeB as XText; 

     if (null != xA && null != xB) 
     { 
      if (xA.Name.LocalName == xB.Name.LocalName) 
       EvenUp(xA, xB); 
      else 
      { 
       Copy_A_To_B(); 
       EvenUp(A, B); // Restart this iteration for various reasons such as 
           // the next nodeA might be the same as current nodeB 
       return; 
      } 
     } 
     else if (null != xA) 
      Copy_A_To_B(); 
     else if (null != tA && null != tB) 
     { 
      if (tA.Value != tB.Value) 
       tB.Value = tA.Value; 
     } 
     else if (null != tA) 
      Copy_A_To_B(); 

     lastB = nodeB; 
    } 
    for (; a < listA.Count; a++) 
    { 
     nodeA = listA[a]; 
     Copy_A_To_B(); 
     if (null == lastB) 
      lastB = B.FirstNode; 
     else 
      lastB = lastB.NextNode; 
    } 
} 

你可以用這個測試:

XElement mainFile = XElement.Load("xmlfile1.xml"); 
XElement langFile = XElement.Load("xmlfile2.xml"); 

EvenUp(mainFile, langFile); 

Console.WriteLine(langFile.ToString()); 
Console.ReadLine(); 

然後,如果您要複製的另一個方向,只需再次而是用參數來調用它切換:EvenUp(langFile, mainFile)那麼這兩個文件應該是重複的。

+0

我還會嘗試你的解決方案,我首先想到了一個遞歸解決方案,因爲它看起來像一個更清潔的方法。明天再測試一下,回覆你,謝謝你的回答! :) – JOSEFtw

+0

這是更好的答案,因爲它是一個可以重用的函數。 – jjthebig1