Parsing Nested Nodes from XML in XDocument - c#

I'm using .net to parse an XML. Below is the XML that needs to be parsed. I need to gather the id from Animal and info from AnimalName, AnimalPicture and AnimalPicture type.
<AnimalEntry version="2.0">
<Animal id="1">
<Information>
<Type>
Indoor Pet
<Type>
<AvailableTypes> //always only 1 type
<AvailableType>
<Active>
<AnimalName> Rupert</AnimalName>
<AnimalPictures> //Always only 1 picture
<AnimalPicture type="jpg"> random.jpg <AnimalPicture>
</AnimalPictures>
</Active>
</AvailableType>
</AvailableTypes>
<Price>10000</Price>
</Information>
</Animal>
</AnimalEntry>
I can gather the id as follows:
XmlDocument xDoc new XmlDocument();
xDoc.Load(xmlUrl);
XmlNode node = xDoc.DocumentElement.ChildNodes[0];
string id = node.Attributes["id"].Value;
I'm not familiar on how to tackle the nested nodes. Is there a recommended way to get the nested AnimalName, AnimalPicture and OwnerId? Do I need to call a foreach or is there a way I can directly get the node?

Use linq to xml.
var xml = XElement.Load(xmlUrl);
int id = (int)xml.Element("Animal").Attribute("id");
var active = xml.Element("Animal")
.Element("Information")
.Element("AvailableTypes")
.Element("AvailableType")
.Element("Active");
var animalName = active.Element("AnimalName").Value;
var animalPictureNode = active.Element("AnimalPictures").Element("AnimalPicture");
var animalPicture = animalPictureNode.Value;
var animalPictureType = animalPictureNode.Attribute("type").Value;
Console.WriteLine(id);
Console.WriteLine(animalName);
Console.WriteLine(animalPicture);
Console.WriteLine(animalPictureType);
Open namespace:
using System.Xml.Linq;

using linq to xml, u can try something like this. If you this approach verbose, you can also try xpath using linq how-to-query-linq-to-xml-using-xpath
XElement element = XElement.Parse("Xml data");
foreach (XElement xe in element.Elements(XName.Get("Animal")))
{
string id = xe.Attribute(XName.Get("id")).Value;
var allChildrens = xe.Descendants();
string animalName = allChildrens.FirstOrDefault(x => x.Name == "AnimalName")?.Value;
string animalPicture = allChildrens.FirstOrDefault(x => x.Name == "AnimalPicture")?.Value;
string animalPictureType = allChildrens.FirstOrDefault(x => x.Name == "AnimalPicture")?.Attribute(XName.Get("type"))?.Value;
}

Related

LINQ xml finding nodes returns null

I try to parse an xml file with XDocument class, with criteria that if the child node matches a given string, its parent node is selected.
<SalesQuotes xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://api.some.com/version/1">
<Pagination>
<NumberOfItems>2380</NumberOfItems>
<PageSize>200</PageSize>
<PageNumber>1</PageNumber>
<NumberOfPages>12</NumberOfPages>
</Pagination>
<SalesQuote>
<Guid>825634b9-28f5-4aa7-98e7-5e4a4ed6bc6a</Guid>
<LastModifiedOn>2018-01-09T12:23:56.6133445</LastModifiedOn>
<Comments>Please note:
installation is not included in this quote
</Comments>
</SalesQuote>
</SalesQuotes>
I tried using
var contents = File.ReadAllText(path: "test1.xml");
var doc = XDocument.Parse(contents);
var root = doc.Root;
var sq = root.Elements("SalesQuote");//return null
var theQuote = root.Elements("SalesQuote").Where(el => el.Element("Guid").Value == "825634b9-28f5-4aa7-98e7-5e4a4ed6bc6a");//return null
var theAlternativeQuote =
from el in doc.Descendants("SalesQuote").Elements("Guid")
where el.Value == "825634b9-28f5-4aa7-98e7-5e4a4ed6bc6a"
select el;//return null
I can't seem to find what's wrong.
Any help is much appreciated! Thanks.
You ignored the namespace bro.
Do remove the xmlns attribute in your XML or try this:
var contents = File.ReadAllText("XMLFile1.xml");
var doc = XDocument.Parse(contents);
var root = doc.Root;
XNamespace ns = "http://api.some.com/version/1";
var sq = root.Descendants(ns + "SalesQuotes"); //return null
var theQuote = root.Elements(ns + "SalesQuote")
.Where(el => el.Element(ns + "Guid").Value == "825634b9-28f5-4aa7-98e7-5e4a4ed6bc6a"); //return null
var theAlternativeQuote =
from el in doc.Descendants(ns + "SalesQuote").Elements(ns + "Guid")
where el.Value == "825634b9-28f5-4aa7-98e7-5e4a4ed6bc6a"
select el; //return null
If you are not too concerned about keeping your current implementation, you could consider using a Typed DataSet and load your XML into fully typed, structured objects.
Querying those objects with Linq will be more straight forward than what I see in your current implementation.
You might also find this useful:
SO Question: Deserialize XML Document to Objects
yap, you're missing the namespace that you can grab with document.Root.GetDefaultNamespace()
// Load
var document = XDocument.Parse(xml);
var xmlns = document.Root.GetDefaultNamespace();
// Find
var query = from element in document
.Descendants(xmlns + "SalesQuote")
.Elements(xmlns + "Guid")
where element.Value == "825634b9-28f5-4aa7-98e7-5e4a4ed6bc6a"
select element;

C# split xml innertext or parse innerxml

I have an XML file with a structure similar to this
<entry name="something">
<members>
<member>aaa</member>
<member>bbb</member>
</members>
</entry>
<entry name="something_else">
<members>
<member>ccc</member>
<member>ddd</member>
</members>
</entry>
I need to be able to get the values out of each of the member nodes to store in a datatable. if i use the innertext property, it concatenates the values (aaabbb). there is nothing discernible to split the string on. I can also use the inner XML but then i just get a string with the XML structure (aaa bbb<\member>)
What is the best way to get each value out of the XML elements and store it in a string array?
here is what I have been trying.
foreach (XmlNode grpNode in GrpList)
{
subNode = grpNode.Attributes["name"];
if (subNode != null)
{
Obj = grpNode.Attributes["name"].Value;
}
subNode = grpNode["members"];
if (subNode != null)
{
string innerXml = string.Empty;
innerXml = grpNode["members"].InnerXml.ToString();
string[] tempArrary = innerXml.Split(new char[] {'>', '<'});
}
}
You can use Xpath to iterate through Entry nodes and get the members within it like this
string xml = "<root><entry name='something'>" +
"<members>" +
"<member>aaa</member>" +
"<member>bbb</member>" +
"</members>" +
"</entry>" +
"<entry name='something_else'>" +
"<members>" +
"<member>ccc</member>" +
"<member>ddd</member>" +
"</members>" +
"</entry></root>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
var memsList = doc.SelectNodes("//entry");
foreach (XmlNode a in memsList)
{
Console.WriteLine(a.Attributes["name"].Value);
var memList = a.SelectNodes("members/member");
foreach(XmlNode x in memList)
Console.WriteLine(x.InnerText);
}
You need to iterate the child elements within members, so something like:
foreach (var node in grpNode["members"].ChildNodes)
{
var value = node.InnerText;
}
That said, you would be better off using LINQ to XML unless you have some specific reason to use XmlDocument. This gives you much more expressive code, for example:
var doc = XDocument.Parse(xml);
var something = doc.Descendants("entry")
.Where(e => (string)e.Attribute("name") == "something")
.Single();
var somethingMembers = something.Descendants("member")
.Select(e => e.Value)
.ToArray();
This should do the trick:
XDocument xdoc = XDocument.Load(#"Path/to/file");
var result = xdoc.Descendants("member").Select (x => x.Value).ToArray();
Result:
Demo Code
the xml you've provided isn't valid. But assuming you just want the inner text of all member nodes into a string array, I'd just use Linq-To-Xml (XDocument):
var results = XDocument.Parse(xmlString)
.Descendants("member")
.Select(m => m.Value)
.ToArray();
Even though you're using the old XmlDocument API, by throwing in an .OfType<XmlNode>() you can convert an XmlNodeList to a generic enumerable and thereby mix in some linq and lambda syntax, for instance:
var tempArrary = subNode.SelectNodes("member").OfType<XmlNode>().Select(n => n.InnerText).ToArray();

How to extract xml child element

I am trying to figure out the code to extract xml child (I think this is worded correctly) elements. I have searched and tried many samples but cannot find how to drill down to pick out the section I want and return the information I need. Maybe I all I need is someone to define the data I am trying to pull so I can read up on the issue, of course any code would be very helpful and I will figure it out from there. Thanks in advanced for any help!
Here is the xml file. I am trying to run an if statement to find the section named <STATISTICTYPE>PVCAP_CharactersSaved</STATISTICTYPE> and return the <JOBNAME>,<TIMEDELTA>,<VALUESUM>.
<?xml version="1.0" encoding="utf-8"?>
<PVCAPTURESTATISTICCONTAINTER xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<PVCAPTUREJOBSTATISTICS>
<PVCAPTURESTATISTICSUMMARY>
<STATISTICTYPE>PVCAP_CharactersSaved</STATISTICTYPE>
<STATISTICNAME>Characters saved</STATISTICNAME>
<JOBID>24</JOBID>
<JOBNAME>HEAT FILES</JOBNAME>
<TIMEDELTA>422</TIMEDELTA>
<VALUESUM>25432</VALUESUM>
</PVCAPTURESTATISTICSUMMARY>
<PVCAPTURESTATISTICSUMMARY>
<STATISTICTYPE>PVCAP_CharactersSaved_NoMM</STATISTICTYPE>
<STATISTICNAME>Characters saved (no match and merge)</STATISTICNAME>
<JOBID>24</JOBID>
<JOBNAME>HEAT FILES</JOBNAME>
<TIMEDELTA>422</TIMEDELTA>
<VALUESUM>25432</VALUESUM>
</PVCAPTURESTATISTICSUMMARY>
</PVCAPTUREJOBSTATISTICS>
<DOCUMENTCOUNT>762</DOCUMENTCOUNT>
<PAGECOUNT>3194</PAGECOUNT>
<IMAGECOUNT>3194</IMAGECOUNT>
<VERSION>2.0</VERSION>
</PVCAPTURESTATISTICCONTAINTER>
You can use LINQ to XML, particularly the XElement class.
var element = XElement.Parse(xmlStr).Element("PVCAPTUREJOBSTATISTICS")
.Elements("PVCAPTURESTATISTICSUMMARY")
.First(c => c.Element("STATISTICTYPE").Value == "PVCAP_CharactersSaved")
var jobName = element.Element("JOBNAME").Value;
var timeDelta = element.Element("TIMEDELTA").Value;
var valueSum = element.Element("VALUESUM").Value;
You'll want to add in some error handling and whatnot here, but this should get you going in the right direction.
You can do something like this:
XElement res = XElement.Parse(xmlResult);
foreach(var elem in res.Element("PVCAPTUREJOBSTATISTICS").Elements("PVCAPTURESTATISTICSUMMARY"))
{
if (elem.Element("STATISTICTYPE").Value.Equals("PVCAP_CharactersSaved", StringComparison.Ordinal))
{
string jobName = elem.Element("JOBNAME").Value;
string timeDelta = elem.Element("TIMEDELTA").Value;
string valueSum = elem.Element("VALUESUM").Value;
}
}
You can use XDocument and LINQ-to-XML to do that quite easily, for example :
string xml = "your xml content here";
XDocument doc = XDocument.Parse(xml);
//or if you have the xml file instead :
//XDocument doc = XDocument.Load("path_to_xml_file.xml");
var result = doc.Descendants("PVCAPTURESTATISTICSUMMARY")
.Where(o => (string) o.Element("STATISTICTYPE") == "PVCAP_CharactersSaved")
.Select(o => new
{
jobname = (string) o.Element("JOBNAME"),
timedelta = (string) o.Element("TIMEDELTA"),
valuesum = (string) o.Element("VALUESUM")
});
foreach (var r in result)
{
Console.WriteLine(r);
}

XDocument does not load Xml string properly

I'm trying to do the following: load an Xml string into a XDocument object, but when I try to access elements through Descendants method it return nothing when I tried to see the value of inner elements in Visual Studio it does not recognize it as Xml so what is the problem here?
string xml = #"<ArrayOfKeyValueOfstringint xmlns=""http://schemas.microsoft.com/2003/10/Serialization/Arrays"" xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
<KeyValueOfstringint>
<Key>crscmprsn_ttlprt1</Key>
<Value>1</Value>
</KeyValueOfstringint>
<KeyValueOfstringint>
<Key>ptntmntrfrm_ttlprt1</Key>
<Value>1</Value>
</KeyValueOfstringint>
</ArrayOfKeyValueOfstringint>";
var xdoc = XDocument.Parse(xml);
IEnumerable<XElement> elements = xdoc.Descendants("KeyValueOfstringint");
var lst = new List<KeyValuePair<string,int>>();
foreach (var item in elements)
{
var k = item.Element("Key").Value;
int v = int.Parse(item.Element("Value").Value);
var kvp = new KeyValuePair<string,int>(k,v);
lst.Add(kvp);
}
You need to specify namespace to get your elements:
var ns = "http://schemas.microsoft.com/2003/10/Serialization/Arrays";
var elements = xdoc.Descendants(ns + "KeyValueOfstringint");
For more information about xml namespaces take a look at: Working with XML Namespaces

How to query a peer XMLNode .NET

I have an xml file. I want to query a peer node in C#.
For ex: For a given input xml file as below,
i want to query the title using the artist as an input. How do i do this ??
i.e Input --> Pink Floyd, Output -----> Division Bell ..
<catalog>
<cd>
<title>Division Bell<title>
<artist>Pink Floyd<artist>
<price>29$<price>
</cd>
<cd>
<title>Relapse<title>
<artist>Eminem<artist>
<price>19$<price>
</cd>
</catalog>
You could use a XDocument:
using System;
using System.Linq;
using System.Xml.Linq;
class Program
{
static void Main()
{
var doc = XDocument.Load("test.xml");
var result =
(from cd in doc.Root.Descendants("cd")
let artist = cd.Element("artist")
let title = cd.Element("title")
where artist != null && title != null && artist.Value == "Pink Floyd"
select title.Value
).FirstOrDefault();
Console.WriteLine(result);
}
}
Its quite easy tor read xml in .net
XDocument loaded = XDocument.Load(#"C:\youxml.xml");
var query = from xElem in loaded.Descendants("cd")
where xElem.Element("artist").Value == "Eminem"
select new Friend
{
Title = xElem.Attribute("title").Value
};
Make use of Linq To XML or XLINQ will resolve you issue easily.
Also check this : http://msdn.microsoft.com/en-us/library/bb308960.aspx
You can also use XPath syntax.
here is an example:
string inputArtist = "Pink Floyd";
var doc = XDocument.Load("test.xml");
XmlNode cdTitleNode = doc.SelectSingleNode("//cd[artist='" + inputArtist + "']/title");
string outputTitle = cdTitleNode.InnerText;
There is an excellent XPATH simulator on http://www.whitebeam.org/library/guide/TechNotes/xpathtestbed.rhtm to try you Xpath expressions.
You can also found a good tutorial there, but a fast search on the web will result in many web pages explaining all about XPath usage and syntax.
Using XPath, you can return a match:
using System.Xml;
using System.Xml.XPath;
public string QuerySearch(string file, string artist) {
// create an XML Document and load the file
XmlDocument xd = new XmlDocument();
xd.Load(file);
// get the root xml element
XmlElement root = xd.DocumentElement;
// get the list of artists
XmlNodeList aristList = root.GetElementsByTagName("artist");
// get the list of titles's
XmlNodeList titleList = root.GetElementsByTagName("title");
// find match
for (int i = 0;i < aristList.Count; i++) {
if (aristList.Item(i).InnerText == artist) {
return titleList.Item(i).InnerText
}
}
return "no match found.";
}

Categories