简体   繁体   English

合并具有更改列表的XML文件

[英]Merging an XML file with a list of changes

I have two XML files that are generated by another application I have no control over. 我有两个我无法控制的应用程序生成的XML文件。 The first is a settings file, and the second is a list of changes that should be applied to the first. 第一个是设置文件,第二个是应应用于第一个的更改列表。

Main settings file: 主要设置文件:

<?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>

And here is an example of the changes: 这是更改的示例:

<?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>

Each variable has an ID and an Optimization that ID applies to. 每个变量都有一个ID和一个ID适用的优化。 Basically, I'm looking to replace the lines where the id="#" and opt="#" are the same with the version from the "change" file. 基本上,我希望将id="#"opt="#"相同的行替换为“ change”文件中的版本。 In the example above, the value for id="15" opt="0" would change from 75 to 425. 在上面的示例中, id="15" opt="0"将从75更改为425。

Would there be any clean way in doing this in C#? 用C#这样做会干净吗? At first thought, reading as text and stepping through the changes using a find-replace type of method seems the cleanest. 乍一看,以文本形式阅读并使用查找替换类型的方法逐步完成更改似乎是最干净的方法。 An approach handling this as an XmlDocument seems like much more work. 将其作为XmlDocument处理的方法似乎需要更多工作。

This would be terribly inefficient if the files get very big, but this is how you can do it with XmlDocuments: 如果文件很大,这将非常低效,但这是使用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" );

Not sure about the efficiency, but this is straightforward with Linq to XML - the follwing was a bit rough - but having remembered that the very wonderful LinqPAD will let you run programs... herewith a complete lump of code that would do the job: 不确定效率,但这对于使用Linq to 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!");
}

Addition of using clauses is left as an exercise :) 剩下的use子句作为练习:)

There's another example here but its in VB which has more capabilities in terms of XML. 这里还有另一个示例但VB中的示例 XML方面具有更多功能。

I also think that it might be possible to do something seriously elegant by way of a query with a join of the two sets of XML data generating a list of dynamic types containing an XElement and the value (or values) that it needs to be updated with. 我还认为,通过将两组XML数据连接起来进行查询,可以做一些非常优雅的事情,从而生成包含XElement和需要更新的一个或多个值的动态类型列表。用。 But I've had enough fun (spent enough time) with this one already for one evening 但是这个晚上我已经有足够的乐趣(花了足够的时间)

Example of doing it in Linq to XML using joins to relate the documents together. 在Linq to XML中使用联接将文档关联在一起的示例。 First I select the elements that match on the two attributes and then I update those to the new value from the change file. 首先,我选择两个属性上匹配的元素,然后从更改文件中将它们更新为新值。

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");

XmlDocument would be ideal for this process and is far less work than the other method you suggested. XmlDocument非常适合此过程,并且比您建议的其他方法少得多的工作量。 You might want to look at other methods to approach this if your using LARGE Xml files as XmlDocument loads the whole document into memory! 如果您将大Xml文件用作XmlDocument会将整个文档加载到内存中,则可能需要查看其他方法来解决此问题!

Anyways the XmlDocument approach would be something like: 无论如何,XmlDocument方法将类似于:

  1. Load both files into their respective XmlDocument objects. 将两个文件加载到各自的XmlDocument对象中。
  2. Iterate through the list of var nodes in the changes file XmlDocument object and foreach one run a xpath query on the original file XmlDocument object to search for the node with the matching id (using the SelectSingleNode() method on the original file XmlDocument object) when found edit the attribute you need edited in the node. 遍历更改文件XmlDocument对象中的var节点的列表,然后让每个人在原始文件XmlDocument对象上运行xpath查询以搜索具有匹配ID的节点(使用原始文件XmlDocument对象上的SelectSingleNode()方法)找到编辑您需要在节点中编辑的属性。
  3. Save the file after all the edits. 完成所有编辑后,保存文件。

I know what i'm telling you to do works but i might not be explaining it clearly. 我知道我要告诉你做的事情,但我可能没有清楚地解释。 I can complete a very rough version of this program in under 30mins and i've only got about a years experience in c#. 我可以在30分钟内完成该程序的非常粗糙的版本,而我在c#中只有大约一年的经验。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM