这是一个棘手的问题。
我有一个文件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
那是因为列表长度不一样,这就是我需要将它添加到列表中的原因......
答案 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)
然后两个文件应该是重复的。