2015-04-22 2 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> 

т.е. объединяет значения на основе атрибута третьего/@ ид ,

Как это сделать с помощью 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"); 
} 

Это производит следующий выходной файл из двух примеров входных файлов в OPS вопрос:

<?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

Спасибо, что указали это. Если бы это был я, то это было жирным пальцем. Ответ отличный. Фунты были ключом. Я адаптировал его и создал цикл 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', а затем сравнить каждое' значение' внутри (поскольку некоторые из них существуют в обоих, но в результате есть только один). –

+0

@CharlesMager ваш правильный, я пропустил это в OP. Работа над исправлением. – DotNetHitMan

+0

@CharlesMager редактирует для сравнения id. – DotNetHitMan

Смежные вопросы