比较两个xmlfiles,添加缺少的元素

时间:2013-10-19 21:18:47

标签: c# xml linq-to-xml

这是一个棘手的问题。

我有一个文件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>

我想更新我的LangFile.XML,使其与我的MainFile.XML匹配,但我需要在LangFile中保留所有Text值。

我希望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级之间的任何东西......

我的问题是我不知道如何在树深处逐行比较行,我尝试了类似的东西,但我卡住了...我不知道如何将特定的后代添加到新的列表。

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 

那是因为列表长度不一样,这就是我需要将它添加到列表中的原因......

2 个答案:

答案 0 :(得分: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)注释行中,您应该管理它。 我希望这会有所帮助。

答案 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)然后两个文件应该是重复的。