Извлечь различия между двумя файлами XML, которые содержат структуры каталогов, используя linQ?
Я реализую клиент-серверную программу, которая синхронизирует файлы между компьютерами. В какой-то момент, когда клиент подключается к серверу, на сервер отправляется файл XML, который содержит структуру каталогов расположения, которое должно быть синхронизировано с сервером. Если это не первый раз, когда клиент подключается, на сервере существуют такие файлы XML, поэтому необходимо найти различия между этими двумя файлами и запросить у клиента только те файлы, которые были изменены или новые. Моя проблема... как я могу найти эти различия между XML. У меня есть 2 образца XML (один XML может иметь и 1Gb).
Первый XML (source.xml):
<?xml version='1.0'?>
<RootDirectory name="New folder" dateCreated="5/20/2013 7:16:32 PM">
<Folder name="New folder1" >
<File name="New Text Document11.txt" />
<File name="New Text Document12.txt" />
</Folder>
<File name="New Text Document1.txt" />
</RootDirectory>
Второй XML (updated.xml):
<?xml version="1.0" encoding="UTF-8"?>
<RootDirectory name="New folder" dateCreated="5/20/2013 7:15:50 PM">
<Folder name="New folder1">
<File name="New Text Document11.txt" />
<File name="New Text Document12.txt" />
</Folder>
<Folder name="New folder2">
<Folder name="New folder21">
<File name="New Text Document211.txt" />
<Folder name="New folder211">
<File name="New Text Document2111.txt" />
<Folder name="New folder2111">
<File name="New Text Document21111.txt" />
</Folder>
</Folder>
</Folder>
<File name="New Text Document21.txt" />
</Folder>
<File name="New Text Document1.txt" />
</RootDirectory>
Я нашел пример, который использует LinQ to XML http://www.codeproject.com/Articles/45233/Diff-in-XML-files-with-LINQ, но используемая там структура XML отличается, и это не помогает мне очень сильно. Учитывая тот факт, что я могу иметь тег Folder
а также File
вложенный в другие узлы.. я действительно не знаю, как найти различия...
Может кто-нибудь дать мне идею?
Спасибо!
С наилучшими пожеланиями, Оана
1 ответ
Возможное решение могло бы быть алгоритмом как это:
- извлечь все папки из старого файла
- извлечь все папки из нового файла
- получать новые папки с помощью метода List Except => new = list2.Except(list1)
- извлекать те же папки с помощью метода Пересечение списка => возможныйКанги = список2.Интерсект (список1)
- сравнить содержимое для измененных списков
- повторить все это (рекурсия или итерация) для всех уровней
Пример кода:
var xml1 = @"<?xml version='1.0'?>
<RootDirectory name=""New folder"" dateCreated=""5/20/2013 7:16:32 PM"">
<Folder name=""New folder1"" >
<File name=""New Text Document11.txt"" />
<File name=""New Text Document12.txt"" />
</Folder>
<File name=""New Text Document1.txt"" />
</RootDirectory>";
var xml2 = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<RootDirectory name=""New folder"" dateCreated=""5/20/2013 7:15:50 PM"">
<Folder name=""New folder1"">
<File name=""New Text Document11.txt"" />
<File name=""New Text Document12.txt"" />
<File name=""New Text Document13.txt"" />
</Folder>
<Folder name=""New folder2"">
<Folder name=""New folder21"">
<File name=""New Text Document211.txt"" />
<Folder name=""New folder211"">
<File name=""New Text Document2111.txt"" />
<Folder name=""New folder2111"">
<File name=""New Text Document21111.txt"" />
</Folder>
</Folder>
</Folder>
<File name=""New Text Document21.txt"" />
</Folder>
<File name=""New Text Document1.txt"" />
</RootDirectory>";
var xmlDoc1 = XDocument.Parse(xml1);
var xmlDoc2 = XDocument.Parse(xml2);
var f1 = allFolders(xmlDoc1.Root);
var f2 = allFolders(xmlDoc2.Root);
var newFolders = f2.Except(f1);
var sameFolders = f2.Intersect(f1);
// completely new folders, content is new => no further checks needed!
newFolders.Dump();
// check if content of same folders has changed
foreach (var sameFolder in sameFolders)
{
var oldContent = folderContent(xmlDoc1.Root.Descendants("Folder").Where (r => r.Attribute("name").Value == sameFolder).ToList());
var newContent = folderContent(xmlDoc2.Root.Descendants("Folder").Where (r => r.Attribute("name").Value == sameFolder).ToList());
var newFiles = newContent.Except(oldContent);
}
private List<string> folderContent(IEnumerable<XElement> nodes)
{
var files = new List<String>();
nodes.ToList().ForEach(n => files.AddRange(n.Elements("File").Select (x => x.Attribute("name").Value).ToList()));
return files;
}
private List<string> allFolders(XElement node)
{
var folders = node.Descendants("Folder").ToList().Select (e => e.Attribute("name").Value).ToList();
return folders;
}
Выход:
//changed folders
New folder2
New folder21
New folder211
New folder2111
// new files for New folder 1
NewTextDocument13.txt