将XML文件与更改列表合并

时间:2009-11-09 20:24:49

标签: c# xml merge

我有两个由我无法控制的应用程序生成的XML文件。第一个是设置文件,第二个是应该应用于第一个的更改列表。

主要设置文件:

<?xml version="1.0"?>
<preset>
  <var id="9" opt="0" val="6666666"/>
  <var id="9" opt="1" val="10000000"/>
  <var id="9" opt="2" val="10000000"/>
  <var id="9" opt="3" val="10000000"/>
  <var id="9" opt="4" val="0"/>
  <var id="10" opt="0" val="4"/>
  <var id="11" opt="0" val="0"/>
  <var id="15" opt="0" val="75"/>
  <var id="22" opt="0" val="0,0,127,516" type="rect(l,t,r,b)"/>
  <var id="23" opt="0" val="27,18,92,66" type="rect(l,t,r,b)"/>
  <var id="24" opt="0" val="320"/>
  ... Skip 300 lines ...
</preset>

这是一个变化的例子:

<?xml version="1.0"?>
<preset>
  <var id="15" opt="0" val="425"/>
  <var id="22" opt="0" val="0,0,127,776" type="rect(l,t,r,b)"/>
  <var id="26" opt="0" val="147"/>
  <var id="27" opt="0" val="147"/>
  <var id="109" opt="1" val="7"/>
  <var id="126" opt="0" val="6,85,85,59" type="crv(t,m,b,vm)"/>
  <var id="157" opt="0" val="1"/>
  ... Skip 10 lines ...
</preset>

每个变量都有一个ID和ID适用的优化。基本上,我希望替换id="#"opt="#"与“更改”文件中的版本相同的行。在上面的示例中,id="15" opt="0"的值将从75更改为425。

在C#中有没有干净的方法?首先想到的是,阅读文本并使用find-replace类型的方法逐步完成更改似乎是最干净的。将此作为XmlDocument处理的方法似乎更多的工作。

4 个答案:

答案 0 :(得分:3)

如果文件变得非常大,这将是非常低效的,但这是你可以用XmlDocuments做到的:

XmlDocument main = new XmlDocument();
main.Load( "main.xml" );

XmlDocument changes = new XmlDocument();
changes.Load( "changes.xml" );

foreach ( XmlNode mainNode in main.SelectNodes( "preset/var" ) )
{
    string mainId = mainNode.Attributes[ "id" ].Value;
    string mainOpt = mainNode.Attributes[ "opt" ].Value;

    foreach ( XmlNode changeNode in changes.SelectNodes( "preset/var" ) )
    {
        if ( mainId == changeNode.Attributes[ "id" ].Value &&
            mainOpt == changeNode.Attributes[ "opt" ].Value )
        {
            mainNode.Attributes[ "val" ].Value = changeNode.Attributes[ "val" ].Value;
        }
    }
}

// save the updated main document
main.Save( "updated_main.xml" );

答案 1 :(得分:2)

不确定效率,但Linq对XML很简单 - 后面的内容有点粗糙 - 但是记得非常精彩的LinqPAD会让你运行程序......这里有一堆完整的代码可以做工作:

void Main()
{
    XDocument settingsXML = XDocument.Load(@"c:\temp\settings.xml");
    XDocument updateXML = XDocument.Load(@"c:\temp\updates.xml");

    Console.WriteLine("Processing");

    // Loop through the updates
    foreach(XElement update in updateXML.Element("preset").Elements("var"))
    {    
        // Find the element to update    
        XElement settingsElement = 
            (from s in settingsXML.Element("preset").Elements("var")
             where s.Attribute("id").Value == update.Attribute("id").Value &&
                   s.Attribute("opt").Value == update.Attribute("opt").Value
             select s).FirstOrDefault();    
        if (settingsElement != null)    
        {      
            settingsElement.Attribute("val").Value = update.Attribute("val").Value;    
            // Handling for additional attributes here
        }    
        else    
        {   
            // not found handling    
            Console.WriteLine("Not found {0},{1}", update.Attribute("id").Value,
                                                   update.Attribute("opt").Value);
        }
    }
    Console.WriteLine("Saving");
    settingsXML.Save(@"c:\temp\updatedSettings.xml");
    Console.WriteLine("Finis!");
}

添加使用条款留作练习:)

还有另一个例子here,但它在VB中具有更多的XML功能。

我还认为通过连接两组XML数据的查询可以做一些非常优雅的事情,生成一个包含XElement和它需要的值(或值)的动态类型列表要更新。但是我已经有足够的乐趣(花了足够的时间)这个已经有一个晚上了

答案 2 :(得分:2)

使用连接将Linq转换为XML以将文档关联在一起的示例。首先,我选择两个属性上匹配的元素,然后将更新文件更新为更改文件中的新值。

XDocument main = XDocument.Load("XMLFile1.xml");
XDocument changes = XDocument.Load("XMLFile2.xml");

var merge = from entry in main.Descendants("preset").Descendants("var")
            join change in changes.Descendants("preset").Descendants("var")
            on 
               new {a=entry.Attribute("id").Value, b=entry.Attribute("opt").Value}
            equals 
               new {a=change.Attribute("id").Value, b=change.Attribute("opt").Value}
            select new
            {
               Element = entry,
               newValue = change.Attribute("val").Value                       
            };

            merge.ToList().ForEach(i => i.Element.Attribute("val").Value = i.newValue);

            main.Save("XMLFile3.xml");

答案 3 :(得分:0)

XmlDocument对于此过程来说是理想的,并且远远少于您建议的其他方法。如果将LARGE Xml文件用作XmlDocument将整个文档加载到内存中,您可能需要查看其他方法来解决此问题!

无论如何,XmlDocument方法将类似于:

  1. 将两个文件加载到各自的XmlDocument对象中。
  2. 遍历更改文件XmlDocument对象中的var节点列表,并在原始文件XmlDocument对象上运行xpath查询,以搜索具有匹配id的节点(使用原始文件XmlDocument上的SelectSingleNode()方法object)当找到编辑你需要在节点中编辑的属性时。
  3. 在所有修改后保存文件。
  4. 我知道我告诉你要做什么,但我可能不会清楚地解释它。我可以在30分钟内完成这个程序的非常粗略的版本,我只有大约一年的c#经验。