使用linQ提取包含目录结构的两个XML文件之间的差异?

时间:2013-05-21 15:05:02

标签: linq-to-xml xmldiff

我正在实现一个在计算机之间同步文件的客户端 - 服务器程序。 在某些时候,当客户端连接到服务器时,包含必须与服务器同步的位置的目录结构的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(changed.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结构有所不同,而且对我没什么帮助。 鉴于我可以将标记FolderFile嵌套在其他节点中。我真的不知道如何找到差异......

有人可以给我一个想法吗?

谢谢!

最诚挚的问候, 瓦娜

1 个答案:

答案 0 :(得分:0)

可能的解决方案可能是这样的算法:

  • 从旧文件中提取所有文件夹
  • 从新文件中提取所有文件夹
  • 使用List Except method =&gt;获取新文件夹new = list2.Except(list1)
  • 使用List Intersect method =&gt;获取相同的文件夹possibleChanges = list2.Intersect(list1)
  • 比较已更改列表的内容
  • 为所有级别重复所有这些(递归或迭代)

示例代码:

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