C# 比较两个XML文件,添加缺少的元素

C# 比较两个XML文件,添加缺少的元素,c#,xml,linq-to-xml,C#,Xml,Linq To Xml,这里有一个棘手的问题 我有一个文件,MainFile.XML,看起来像这样: <xml> <header> <some></some> <addThis></addThis> </header> <footer></footer> <this> <is> <deep> <lik

这里有一个棘手的问题

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

这是因为列表的长度不同,这就是我需要将其添加到列表中的原因…

据我所知,考虑到两个文件的结构相似,您要做的是用另一个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)注释的行中,您应该管理它。
我希望这有帮助。

您可能希望递归地执行此操作。我想简单的文件拷贝不是你想要的

猜猜写这封信的时候,你查了另一封作为答案。我希望它对您有效,但这里有另一种方法。我的方法看起来更复杂,所以如果马里亚诺的方法对你有效,那就太好了

/// <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;
    }
}

如果要复制另一个方向,只需再次调用它,但参数已切换:
EvenUp(langFile,mainFile)
那么两个文件都应该是重复的。

可以有多少项?多少筑巢?有ID值吗?如果两个文本值不同,会发生什么情况?主文件将永远不会有任何文本值。嵌套是灵活的,它可以是1-1000级之间的任何级别…1000级?你不应该在那里使用XML,你可能在这里看到一个递归解决方案。嗯…不是1000,但它可以是1-至少5-6级之间的任何级别。我需要使用Xml,因为它的langfiles用于Episerver。是的,我想做以下事情:有一个MainFile.Xml,在这个文件中,我可以手动添加行。然后我想将我的LangFile.XML与主文件进行比较,如果主文件中的行在LangFile中不存在,我想添加它。当我有更多的时间时,我会看看你的代码,但是看起来不错!工作起来很有魅力!谢谢,我也要试试你的解决方案,一开始我在考虑一个递归解决方案,因为它看起来像一个更干净的方法。明天将对此进行测试并返回给您,谢谢您的回答!:)这是一个更好的答案,因为它是一个可以按原样重用的函数。
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 
   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);
    }
/// <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();