Getting to "bottom" of XmlDocument - C# - c#

I have two versions of XmlDocument
Version 1
<?xml version="1.0" encoding="UTF-8"?>
<topElement>
<childElement1>Value</childElement1>
<childElement2>Value</childElement2>
...
</topElement>
Version 2
<?xml version="1.0" encoding="UTF-8"?>
<topElement>
<group1>
<childElement1>Value</childElement1>
<childElement2>Value</childElement2>
</group1>
<group2>
<childElement1>Value</childElement1>
<childElement2>Value</childElement2>
</group2>
</topElement>
In both occasions I need to get all the values for all childElements and add them to collection of a CustomObject.
As far as I understand this could be done through iteration only.
So I get the top node, and go like this:
CustomObject getLow(XmlNode node, CustomObject customObject)
{
foreach (XmlNode n in node.ChildNodes)
{
if (n.HasChildNodes == true)
{
getLow(n);
}
customObject.collection.Add(n.Name, n.InnerText);
}
return customObject;
}
No doubt it is wrong, please can somebody help me getting the correct result in both cases?

You can use Xpath with your XmlDocument:
XmlDocument xmlDoc = new XmlDocument("yourxml.xml");
foreach (XmlNode childElement in xmlDoc.SelectNodes("//childElement"))
{
customObject.collection.Add(childElement.Name, childElement.InnerText);
}

Looping isn't the only way - another way would be to generate an object graph of custom classes with XML attributes and use the XML serilizer to convert the XML into an object graph.
See http://msdn.microsoft.com/en-gb/library/system.xml.serialization.xmlserializer.aspx for details.
An alternative to that would be to use an XDocument and use linq to query the values you want straight out of the XDocument.
See Using Linq and XDocument, can I get all the child elements under parent tag?
Hope this helps

You should be able to use some xPath to get the nodes you are looking for.
Try something like
node.SelectNodes("//*[count(child::*)=0]")

Related

Extract data from a XML string using linq vs xmlDocument

I have done the below many times using a xmlDocument approach, but I wanted to use the more powerful linq to xml approach.
However, I seem to have run into a wall.
I am getting data back from a restful API from twillio / crmText.
Here is a link to their site where their docs live:
http://crmtext.com/api/docs
Here is my XML string:
<response op="getcustomerinfo" status="200" message="ok" version="1.0">
<customer>
<totalMsg>3</totalMsg>
<custId>9008281</custId>
<custName></custName>
<timestamp>2015-04-30 16:17:19</timestamp>
<optinStatus>3</optinStatus>
<custMobile>6185551212</custMobile>
<subacct>1st Choice Courier</subacct>
</customer>
</response>
I need to find out the optinStatus. It should return 3.
I am using the below, which return the above xml
XDocument xdoc = XDocument.Parse(result1);
I have tried about 4000 different things, including:
IEnumerable<XElement> otinStatus = from el in xdoc.Elements("customer") select el;
IEnumerable<XElement> otinStatus2 = from el in xdoc.Elements("cusotmer.optinStatus") select el;
IEnumerable<XElement> otinStatus3 = from el in xdoc.Elements("optinStatus") select el;
All of which returns no results.
Please help, I know this is something simple I am missing.
Thank you in advance -- Joe
Retrieve an Element's Value
var status = xDoc
.Descendants("optinStatus") // get the optinStatus element
.Single() // we're expecting a single result
.Value; // get the XElement's value
Example
Here is a working Fiddle for you. You can see it running live here. The output is 3.
using System;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
public class Program
{
public static void Main()
{
var xDoc = XDocument.Parse(xmlString);
var status = xDoc.Descendants("optinStatus").Single();
Console.WriteLine(status.Value);
}
private static string xmlString = #"
<response op=""getcustomerinfo"" status=""200"" message=""ok"" version=""1.0"">
<customer>
<totalMsg>3</totalMsg>
<custId>9008281</custId>
<custName></custName>
<timestamp>2015-04-30 16:17:19</timestamp>
<optinStatus>3</optinStatus>
<custMobile>6185312349</custMobile>
<subacct>1st Choice Courier</subacct>
</customer>
</response>
";
}
Explanation
Descendents() is an instance axes method (or just axes in shorthand). It returns an IEnumerable<XElement> of all matching descendents. On its results, we call Single(). It is a Linq method that returns the only element of a sequence. If there is more than one element, it throws an error. We're left with a single XElement. This represent an entire XML element. Since we only want its value not the entire element, we call the Value property. Bingo, we're done.
A Bit More Detail
Axes come in two kinds:
Instance axes methods, which MSDN lists here, are invokable members of the XElement, XDocument, and XNode classes.
Extension axes methods, which MSDN lists here, are invokable on collections.
With one exception, an axes method returns a collection of type IEnumerable<T>. The exception is Element(), which returns the first matching child object. That what AmatuerDev used and, as in your question, if you are only expecting a single result, it is a just as good if not better approach that is Descendants().
Retrieve an Attribute Value
Once we have an XElement, we can retrieve one of its attributes instead of its value. We do that by calling the Attributes() method. It returns the matching XAttribute. Since we only want the attribute value, we call the Value property. Voila.
// for attribute
var response = xDoc.Descendants("response").Single();
var attr = response.Attribute("status");
General Approach
Using Linq to XML is a two step process.
Call an axes method to obtain an IEnumerable<T> result.
Use Linq to query that.
See Also
Here is some relevant MSDN documentation:
How to: Retrieve the Value of an Element (LINQ to XML)
LINQ to XML Axes
Assuming xDoc being the XDocument. Have you tried..
var customer = xDoc.Root.Element("customer");
var optinStatus = customer.Element("optinStatus");
var optinStatusValue = optinStatus.Value;

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

Check all the children for XElement

I have XElement object which is my XML tree read from XML file. Now I want to check all the nodes in this tree to get first attribute name and value. Is there any simple way to go through all of the nodes (from root till leaves)? My XML file has got very many different and strange nodes - that's why it's harder to solve this issue. I thought about writing some recursion, but hope it's another way to solve that easier.
Maybe take a look to Xpath. an XPath like this //*[#id=42] could do the job.
It means get all nodes which have an attribute "id" of value 42.
You can do just //* which gonna returns all nodes in a tree.
Xpath :
http://msdn.microsoft.com/en-gb/library/ms950786.aspx
Syntax :
http://msdn.microsoft.com/en-us/library/ms256471.aspx
You can get all children elements using XElement.Elements().
Here's some code using recursion to get all elements of each level:
void GetElements(XElement element){
var elements = element.Elements();
foreach(Element e in elements){
//some stuff here
if(e.Elements() != null)
GetElements(e);
}
}

Read child elements using C# from xml

Greeting,
What is the best practice to read all attributes from a child elements by ID attributes using C# in xml file listed down.
Thank you,
<?xml version="1.0" encoding="utf-8"?>
<WinDLN>
<Program ID="1" Name="CIS562" StartDate="9/8/2010 5:50:00 PM" EndDate="9/8/2010 9:15:00 PM" />
<Program ID="2" Name="CIS532" StartDate="10/8/2010 5:50:00 PM" EndDate="10/8/2010 9:15:00 PM" />
<Program ID="3" Name="ECE552" StartDate="6/8/2010 5:50:00 PM" EndDate="6/8/2010 9:15:00 PM" />
</WinDLN>
The following LINQ call should do the trick:
var attrs =
doc.Descendants("Program").First(prog =>
prog.Attribute("ID").Value == "2").Attributes();
The Descendants method gives you all elements (anywhere) in the XML document that are named "Program". Using First, you can get the first one that matches some specified predicate (e.g. has "ID" equal to "2"). Note that you can use FirstOrDefault if you want to get null when there is no such element. Finally, Attributes gives you a collection of all attribtues of the element.
I think that using LINQ to XML if you can is preferrable - you'll write the same code when working with XML or other data sources, so reading and writing the code is easy (once you learn LINQ).
There are many ways to do it, e.g. LINQ over XML. But using Xpath is definitely not dead yet:
class Program
{
static void Main(string[] args)
{
XmlDocument doc = new XmlDocument();
string xml = #"... your xml ";
doc.LoadXml(xml);
// Using SelectNodes with Xpath
XmlNodeList list = doc.SelectNodes("WinDLN/Program[#ID='2']");
Console.WriteLine(list.Count); // prints 1
list = doc.SelectNodes("WinDLN/Program[#ID]");
Console.WriteLine(list.Count); // prints 3 (selected all IDs)
}
}
What method you'll choose is most often a matter of taste, select the API you're most comfortable with.

Merging similar xml documents

I have several XDocuments that look like:
<Test>
<element
location=".\jnk.txt"
status="(modified)"/>
<element
location=".\jnk.xml"
status="(overload)"/>
</Test>
In C#, I create a new XDocument:
XDocument mergedXmlDocs = new XDocument(new XElement("ACResponse"));
And try to add the nodes from the other XDocuments:
for (ti = 0; (ti < 3); ++ti)
{
var query = from xElem in xDocs[(int)ti].Descendants("element")
select new XElement(xElem);
foreach (XElement xElem in query)
{
mergedXmlDocs.Add(xElem);
}
}
At runtime I get an error about how the Add would create a badly-formed document.
What am I doing wrong?
Thanks...
(I saw this question -- Merge XML documents -- but creating an XSLT transform seemed like extra trouble for what seems like a simple operation.)
You are very close. Trying changing the line
mergedXmlDocs.Add(xElem);
to
mergedXmlDocs.Root.Add(xElem);
The problem is that each XML document can only contain 1 root node. Your existing code is trying to add all of the nodes at the root level. You need to add them to the existing top level node instead.
I am not sure what programming language you are using, but for most programming languages there is extensive XML support classes. Most of them allow parsing and even adding of element. I would have 1 main file that I would keep around and then parse each new one adding the elements from the new one into the master.
EDIT: Sorry it looks like you are already doing exactly this.

Categories