2015-04-22 52 views
1

我想要合併成單個文件的多個XML文件。 Linq to XML可能是最好的選擇,但我對想法持開放態度(XSLT在合併兩個文件時似乎很好,但在n> 2或n =大的情況下很笨拙)。使用linq合併具有相同結構的多個XML文件並刪除基於密鑰的重複項

從閱讀其他問題來看,某種連接看起來不錯。

File1.xml:

<first> 
    <second> 
    <third id="Id1"> 
     <values> 
     <value a="1" b="one"/> 
     <value a="2" b="two"/> 
     <value a="3" b="three"/> 
     </values> 
    </third> 
    <third id="Id2"> 
     <values> 
     <value a="f" b="foo"/> 
     <value a="b" b="bar"/> 
     <value a="w" b="wibble"/> 
     </values> 
    </third> 
    </second> 
</first> 

File2.xml:

<first> 
    <second> 
    <third id="Id1"> 
     <values> 
     <value a="2" b="two"/> 
     <value a="3" b="three"/> 
     <value a="6" b="six"/> 
     </values> 
    </third> 
    <third id="Id3"> 
     <values> 
     <value a="x" b="ex"/> 
     <value a="y" b="why"/> 
     <value a="z" b="zed"/> 
     </values> 
    </third> 
    </second> 
</first> 

Merged.xml:

<first> 
    <second> 
    <third id="Id1"> 
     <values> 
     <value a="1" b="one"/> 
     <value a="2" b="two"/> 
     <value a="3" b="three"/> 
     <value a="6" b="six"/> 
     </values> 
    </third> 
    <third id="Id2"> 
     <values> 
     <value a="f" b="foo"/> 
     <value a="b" b="bar"/> 
     <value a="w" b="wibble"/> 
     </values> 
    </third> 
    <third id="Id3"> 
     <values> 
     <value a="x" b="ex"/> 
     <value a="y" b="why"/> 
     <value a="z" b="zed"/> 
     </values> 
    </third> 
    </second> 
</first> 

即,它基於所述第三/ @ id屬性合併的值。

我該如何優雅地用linq做這件事?

回答

3

下面仍然是相當難看,而且我相信它可以接觸到一些工作的一個較爲流線型的造型,但現在這似乎做的工作:

public static void MergeXml() 
{ 
    var xdoc1 = XDocument.Load(@"c:\temp\test.xml"); 
    var xdoc2 = XDocument.Load(@"c:\temp\test2.xml"); 
    var d1Targets = xdoc1.Descendants("third"); 
    var d2Selection = xdoc2.Descendants("third").ToList(); 

    Func<XElement, XElement, string, bool> attributeMatches = (x, y, a) => 
     x.Attribute(a).Value == y.Attribute(a).Value; 

    Func<IEnumerable<XElement>, XElement, bool> hasMatchingValue = (ys, x) => 
     // remove && if matching "a" should cause replacement. 
     ys.Any(d => attributeMatches(d, x, "a") && attributeMatches(d, x, "b")); 

    foreach (var e in d1Targets) 
    { 
     var fromD2 = d2Selection.Find(x => attributeMatches(x, e, "id")); 
     if (fromD2 != null) 
     { 
      d2Selection.Remove(fromD2); 
      var dest = e.Descendants("value"); 
      dest.LastOrDefault() 
       .AddAfterSelf(fromD2.Descendants("value").Where(x => !hasMatchingValue(dest, x))); 
     } 
    }; 
    if (d2Selection.Count > 0) 
     d1Targets.LastOrDefault().AddAfterSelf(d2Selection); 

    xdoc1.Save(@"c:\temp\merged.xml"); 
} 

這將產生從兩個例子輸入文件下面的輸出文件中有機磷農藥的問題:

<?xml version="1.0" encoding="utf-8"?> 
<first> 
    <second> 
    <third id="Id1"> 
     <values> 
     <value a="1" b="one" /> 
     <value a="2" b="two" /> 
     <value a="3" b="three" /> 
     <value a="6" b="six" /> 
     </values> 
    </third> 
    <third id="Id2"> 
     <values> 
     <value a="f" b="foo" /> 
     <value a="b" b="bar" /> 
     <value a="w" b="wibble" /> 
     </values> 
    </third> 
    <third id="Id3"> 
     <values> 
     <value a="x" b="ex" /> 
     <value a="y" b="why" /> 
     <value a="z" b="zed" /> 
     </values> 
    </third> 
    </second> 
</first> 
+0

輝煌。謝謝。 –

+0

@IainHolder我看到你沒有接受這個答案。評論爲什麼這不/不再回答你的問題,將不勝感激。 – Alex

+0

感謝您指出。如果是我,那是很胖的手指。答案很好。這些funcs是關鍵。我調整了它並創建了一個for循環,將相當多的文件放在一起。我已經重新接受了答案。 :-) –

1

這應該合併第二個文件的「內容」到第一個使用LINQ。

 XDocument document = XDocument.Load("File1.xml"); 
     XElement secondElement = document.Descendants("second").FirstOrDefault(); 

     XDocument document2 = XDocument.Load("File2.xml"); 
     XElement secondlement2 = document2.Descendants("second").FirstOrDefault(); 

     secondElement.Add(secondlement2.Nodes()); 

UPDATE - 增加了下面的代碼,以滿足最終輸出的唯一條目。

 XDocument document = XDocument.Load(@"File1.xml"); 
     XElement secondElement = document.Descendants("second").FirstOrDefault(); 

     XDocument document2 = XDocument.Load(@"File2.xml"); 
     XElement secondlement2 = document2.Descendants("second").FirstOrDefault(); 

     var startingNodes = (from n2 in secondElement.Descendants("third") select n2.Attribute("id").Value).ToList(); 

     var nonUniqueNodes = (from n in secondlement2.Descendants("third") where startingNodes.Contains(n.Attribute("id").Value) select n).ToList(); 

     secondElement.Add(secondlement2.Elements().Except(nonUniqueNodes)); 
+2

你不會得到您所請求的'Merged.xml'文件,結果任何東西,雖然。你需要匹配每個'third'元素,然後比較每個'value'(因爲兩者都存在於兩者中,但結果中只有一個)。 –

+0

@CharlesMager你的錯,我錯過了OP。正在進行更正。 – DotNetHitMan

+0

@CharlesMager編輯以容納id比較。 – DotNetHitMan

相關問題