How do I modify a XML Node? - c#

I want to modify the Node my XML File look like this.
<Tasks>
<Task>
<Title>Title of the Task</Title>
<Description>Description of the Task</Description>
<Done>false</Done>
</Task>
<Task>
<Title>Title of anotherTask</Title>
<Description>Description of anotherTask</Description>
<Done>true</Done>
</Task>
</Tasks>
I could adress the Node like this:
xmlDoc.SelectSingleNode("/Tasks/Task/Description").InnerText = "My Description";
However I have mulitple Tasks. How do I indicate which is which? I want to change the State of the Task "Done" from false to true.

You could iterate through each resulting node from something like this:
foreach( XmlNode xn in xmlDoc.SelectNodes("//Tasks"))
{
// Do something
}
Doing what you need to do on each node. More info on SelectNodes here: https://msdn.microsoft.com/en-us/library/system.xml.xmlnode.selectnodes%28v=vs.110%29.aspx
If you have control over the design of the XML, perhaps you should consider adding an ID to your task. An ID will allow you to make changes to an specific Task node instead of iterating through them or looking up by Task.Title.
You may also look at these articles:
https://msdn.microsoft.com/en-us/library/bb943906.aspx
How can I iterate though each child node in an XML file?
Having said all this, I feel your question is missing some more information on what is the criteria on when to do that something correctly. Could you expand some more? You will get better answers that way.

Related

xpath to element with attribute and grandparent attribute

The relevant chunk of my xml is this:
[... lots of xml up here, including ancestor elements...]
<category id="MyCatID" ... >
<option ... >
<property id="MyPropID">The magic value I need</property>
[... lots of xml down here...]
My objective: Find the value of a <property> with id of MyPropID whose parent is <option> and whose grandparent (through <option>) is <category> containing the id of MyCatID.
Here is my attempted xpath:
//property[#id='MyPropID']/ancestor::category[#id='MyCatID']
In my .NET 4.7.2 that xpath query brings back all the xml inside the <category> element, which misses the mark. My hoped-for result is that it would bring back the value The magic value I need.
How is it done?
Why not reverse it, get the category with the ID you want and then navigate to the property with the ID you want? I'm not really sure how your XML looks, here's my pseudo attempt...
//category[#id='MyCatID']/option/property[#id='MyPropID']
And if for some reason you would really want to do it bottom-up way:
//property[#id='MyPropID']/../../../property[#id='MyPropID']
or
//property[#id='MyPropID']/ancestor::node()[3]/property[#id='MyPropID']

XDocument - Iterating Over XML Elements

Given an example XML file as such:
<libraries>
<library name="some library">
<book name="my book"/>
<book name="your book"/>
</library>
<library name="another library">
<book name="his book"/>
<book name="her book"/>
</library>
</libraries>
How would one iterate through each library and get only its children? E.g. if I was in the first library element and I went to retrieve all its descendants/children, it would only return with the two books inside it.
I've tried iterating and using XElement.Elements("book"), XElement.Elements(), XElement.Descendants(), etc. but all return every element that is a book (so it would pull the elements from the second library, too). Mostly I think I'm just struggling with understanding how XDocument keeps track of its elements and what's considered a descendant/child.
If possible, if one could explain as to how this would be done with XDocument for an element at any level it'd be appreciated (e.g. if each book had child elements, and if those elements had child elements, etc).
You can iterate over your XML by going through all the descendents of libraries in the following way.
XDocument doc=XDocument.Load(XmlPath);
foreach (var item in doc.Descendants("library"))
IEnumerable<XNode> nodes = item.DescendantNodes();//Here you got book nodes within a library
Sheer,
The problem is you are pulling all elements with "book".
If you want to get only items dependant on the parent element, you will have to supply a proper condition.
var v = from n in doc.Descendants("library")
where n.Attribute("name").Value == "some library"
select n.DescendantNodes();
Now, this will give you element who's name is "some library".

Cant go deeper than root, LINQ to (Funds)XML, C#

I'm working with a specific FundsXML-Schema trying to get all Assetss of a specific XML-File to iterate through.
Short example of xml-file:
<?xml version="1.0" encoding="utf-8"?>
<FundsXML xmlns="http://www.fundsxml.org/XMLSchema/3.0.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="3.0.5" xsi:schemaLocation="http://www.fundsxml.org/XMLSchema/3.0.5 FundsXML3.0.5.xsd">
<Date>2015-02-27</Date>
...
<AssetMasterData>
<Asset>
<SecurityCodes>
<ISIN>XXXXXXXXXXXX</ISIN>
</SecurityCodes>
</Asset>
...
<Asset>
</AssetMasterData>
</FundsXML>
I want to iterate through Assets in there. I tried:
XDocument xmlTree = XDocument.Load(xmlPath);
XElement root = xmlTree.Root;
foreach (XElement f in root.Descendants())
{
System.Windows.MessageBox.Show(f.Name.ToString() +" ; "+f.Value.ToString());
}
Output: {http://www.fundsxml.org/XMLSchema/3.0.5}Date ; 2015-02-27
The second part would be to read ISIN of each Asset node.
But I hadn't time to do this, because I'm failing at the first part.
EDIT:
Solution was to search for namespace+name:
foreach (XElement f in root.Descendants("{http://www.fundsxml.org/XMLSchema/3.0.5}Asset"))
Best solution in my opinion:
foreach (XElement f in root.Descendants(xmlTree.Root.GetDefaultNamespace()+"Asset"))
As your XML is in a namespace, you need to add the namespace information to the Descendants query.
You can see an example here
You can try to get the
roots.Descendants()
Without filtering and check the nodes that it returns to confirm this.
Based on the sample data you've provided
<Asset></Asset>
doesn't appear to have any data in it. You would need to get
foreach (XElement f in root.Descendants("ISIN"))
I think anyway. If there's no actual text then you will get a blank or empty value?? So it sounds like it's returning what you're asking for??

What's a better alternative to the following data structure: Dictionary<string, Dictionary<string, string>>

I have the following set of data
<ids>
<id1 attr1="value1" attr2="value2" />
<id2 attr3="value3" attr4="value4" />
<id3 attr2="value6" attr5="value7" />
</ids>
Basically, it's an XML that can have any node name with any attribute name with any attribute value.
After parsing the XML, I store the attribute data in a Dictionary.
Then I store that same Dictionary as a value with the node name as a key.
So my data structure would be a Dictionary<string, Dictionary<string, string>> (let's give this a variable name called "dict")
So if I wanted to get the value for attr2 in the id1 node, I would do:
string value = dict["id1"]["attr2"];
// value will be value2
I think this is a pretty simple and workable solution for my needs, but there just seems to be this voice at the back of my head telling me that there is a different data structure or simpler solution that I'm missing out on. What does everyone think?
I think your solution is a good one. It will provide very fast lookups, and matches exactly to your domain.
Is your main problem with the nested dictionaries? If so, I would suggest that you not worry about it - using collections of collections is often a very useful tool.
My only complaint would be this: If you're not using this frequently, you're going to be loading a lot of information into a data structure that may be unncessary. If this is for one time lookups, leaving it in XML and using XPath queries may be a more optimal solution than pre-parsing and loading the entire thing into memory. If you're querying this frequently, though, this is a more optimal solution.
How about a class?
public class YourId
{
public string Id { get; set; }
public string Attribute1 { get; set; }
public string Value { get; set; }
}
Then you could create a List and populate it via your xml...
It would be easy to work with and you could use linq with it:
List<YourId> items = GetIdsFromXml();
var query = from i in items
where i.Id == "id1"
select i;
// or...
items.Where(i => i.Attribute == "blah").ToList();
// ect.
Just for grins - what if you kept the XML DOM and found your attributes with XPath queries? That way if you had duplicate node names you could accomodate that.
That XML doesn't look very good. It's not semantic XML at all. Semantic XML would be:
<data>
<item id="id1">
<value name="attr1">value1</value>
<!-- ... -->
</item>
<!-- ... -->
</data>
I know it's bigger, but that's XML for you. The reason I'm even saying this is that if you're not ready to go with semantic XML, you're probably looking for another data format. XML is a little bit bloated by nature. If you're looking for a compact format, have a look at JSON.
Anyways, using semantic XML, I would recommend XPath. Have a look in the MSDN documentation and look at the SelectNodes methods in the DOM objects.
Short example:
XmlDocument doc = new XmlDocument();
doc.Load("data.xml");
// Get a single item.
XmlNode item = doc.SelectSingleNode("//item[#id=myid]");
As long as all of the nodes have unique names, you should be OK. Note that it won't really work for XML like this:
<nodes>
<node id="id1" attr1="value1" attr2="value2" />
<node id="id2" attr3="value3" attr4="value4" />
<node id="id3" attr2="value6" attr5="value7" />
</nodes>
Given that the XML can have any node name and any attribute name I think your current solution is optimal.
Why not to use something that already exists?
Like Simple XML Parser in C#
If you need an XML grammar then create one for your needs. If you need a parser then use one of the many excellent ones provided in the .Net library. If you need to store the document in memory and access it use the DOM and XPath to select nodes. If you don't need any of this, then I would recommend against using XML and instead using something simpler like JSON.
If you need to keep the whole thing in memory, but just the values, then I suggest using the DataSets and loading them with the the XML loaders.

How can I use XPath to get elements?

My XML is like:
<root>
<section name="blah">
<item name="asdf">2222</item>
</section>
</root>
I will have multiple 'sections' in the XML, I want to fetch a particular section.
In this case, I need to get items that are in the section named "blah".
The xpath is then:
/root/section[#name='blah']/item
for example, in XmlDocument:
foreach(XmlElement item in doc.SelectNodes("/root/section[#name='blah']/item"))
{
Console.WriteLine(item.GetAttribute("name"));
Console.WriteLine(item.InnerText);
}
Edit re comments: if you just want the sections, then use:
/root/section[#name='blah']
but then you'll need to iterate the data manually (since you can theoretically have multiple sections named "blah", each of which can have multiple items).

Categories