Reputation: 13907
I have two XML files that are generated by another application I have no control over. 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. Basically, I'm looking to replace the lines where the id="#"
and opt="#"
are the same with the version from the "change" file. In the example above, the value for id="15" opt="0"
would change from 75 to 425.
Would there be any clean way in doing this in 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.
Upvotes: 0
Views: 2134
Reputation: 4339
Example of doing it in Linq to XML using joins to relate the documents together. 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");
Upvotes: 2
Reputation: 10190
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:
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 :)
There's another example here but its in VB which has more capabilities in terms of 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. But I've had enough fun (spent enough time) with this one already for one evening
Upvotes: 2
Reputation: 1
XmlDocument would be ideal for this process and is far less work than the other method you suggested. 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!
Anyways the XmlDocument approach would be something like:
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#.
Upvotes: 0
Reputation: 7598
This would be terribly inefficient if the files get very big, but this is how you can do it with 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" );
Upvotes: 3