How to parse nested XML in C# - c#

I'm working with an API and retrieving the data in XML. Here's my XML:
<RTT>
<AgencyList>
<Agency Name="Caltrain" HasDirection="True" Mode="Rail">
<RouteList>
<Route Name="BABY BULLET" Code="BABY BULLET">
<RouteDirectionList>
<RouteDirection Code="SB2" Name="SOUTHBOUND TO TAMIEN">
<StopList>
<Stop name="Sunnyvale Caltrain Station" StopCode="70222">
<DepartureTimeList/>
</Stop>
</StopList>
</RouteDirection>
<RouteDirection Code="NB" Name="NORTHBOUND TO SAN FRANCISCO">
<StopList>
<Stop name="Sunnyvale Caltrain Station" StopCode="70221">
<DepartureTimeList>
<DepartureTime>69</DepartureTime>
</DepartureTimeList>
</Stop>
</StopList>
</RouteDirection>
</RouteDirectionList>
</Route>
<Route Name="LIMITED" Code="LIMITED">...</Route>
<Route Name="LOCAL" Code="LOCAL">...</Route>
</RouteList>
</Agency>
</AgencyList>
</RTT>
Not every DepartureTimeList will have a DepartureTime child node. Here's what I got so far, which retrieves the Route name:
List<string> trainType = new List<string>();
XDocument doc = XDocument.Load("http://services.my511.org/Transit2.0/GetNextDeparturesByStopName.aspx?token=0f01ac4a-bc16-46a5-8527-5abc79fee435&agencyName=Caltrain&stopName=" + DropDownList1.SelectedItem.Text.ToString());
doc.Save("times.xml");
string feed = doc.ToString();
XmlReader r = XmlReader.Create(new StringReader(feed));
r.ReadToFollowing("RouteList");
if (r.ReadToDescendant("Route"))
{
do
{
trainType.Add(r.GetAttribute("Name"));
} while (r.ReadToNextSibling("Route"));
}
I'm mostly interested in the departure time (if it exists) and I've been struggling all afternoon to try and parse it.

Try this... Hopefully this will do it.
XmlDocument doc = new XmlDocument();
doc.Load("xml path");
XmlNode node = doc.SelectSingleNode("/RTT");
foreach (XmlNode nodes in node.SelectNodes(
"/AgencyList/Agency Name/RouteList/Route"))
{
trainType.Add(r.GetAttribute("Name"));
XmlNode s = nodes.SelectSingleNode("Route Name/RouteDirectionList/RouteDirection Code/StopList/Stop");
if (s != null && s["DepartureTimeList"].HasChildNodes)
{
// do stuff here
}
}

Related

Fetching XML child values in c#

I have some data i need to fetch from a xml file.
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
How would I fetch the year based of the category?
So it would be like
writeline( FetchYearFromCategory("cooking") );
and it would output 2005
Hope you can understand
You can use LINQ to Xml
// Load xml in the memory
var document = XDocument.Load(pathToXmlFile);
// Filter xml to find required data
var yearsOfCooking =
document.Descendants("book")
.Where(book => book.Attribute("category").Value == "cooking")
.Select(book => book.Element("year").Value);
// Print result
foreach (var year in yersOfCooking)
{
Console.WriteLine(year);
}
The tool you are looking for is called XPath. You can define which elements are going to match using a relatively easy syntax. W3Schools has a pretty good tutorial, if you are not already familiar with it. Here is the link.
The syntax to use it would be:
// This will fetch all "book" items in the root node
// that have the category = "cooking"
// and select their year node.
var strXPathExpression = "/book[#category='cooking']/year";
var doc = new XmlDocument();
doc.Load("YourXmlFile.xml");
var root = doc.DocumentElement;
var desiredNodes = root.SelectNodes(strXPathExpression).
foreach (var node in desiredNodes)
{
// You have your year here
var year = node.Value;
}
You can use System.Xml to access XML Files in C#.NET
public int FetchYearFromCategory(string category){
XmlDocument doc = new XmlDocument();
doc.Load("path_to_your_file");
XmlNodeList allBooks = doc.GetElementsByTagName("book");
foreach (XmlNode singleBookNode in allBooks)
{
if(singleBookNode.Attributes["category"] != null && singleBookNode.Attributes["category"].Value.Equals(category))
{
XmlNodeList childNodes = singleBookNode.ChildNodes;
foreach (XmlNode childNode in childNodes)
{
if(childNode.Name.Equals("year"))
{
return Int32.Parse(childNode.InnerText);
}
}
}
}
return -1;}
I think the above should do you the job.

c# adding xml element where attribute equals

I have an xml file that has a bunch of channels, and I want to append a channel category to every single one of them. Depending on what channel it is. I'm very new to this so please excuse me if this is an obvious error.
example:
<channel-category>Entertainment</channel-category>
or
<channel-category>News</channel-category>
I have tried the following:
string path;
string xmlfile = "/channels.xml";
path = Environment.CurrentDirectory + xmlfile;
if (exists("channelname1"))
{
XmlDocument doc = new XmlDocument();
doc.Load(path);
XmlNode root = doc.DocumentElement;
XmlNode node = root.SelectSingleNode("list/channel[#id='channelname1'");
XmlNode category = doc.CreateElement("channel-category");
category.InnerText = "channelcataegorygoeshere";
node.AppendChild(category);
doc.DocumentElement.AppendChild(node);
}
else
{
Console.WriteLine("not found");
}
Console.ReadKey();
}
static bool exists(string channelname)
{
string path;
string xmlfile = "/channels.xml";
path = Environment.CurrentDirectory + xmlfile;
XDocument xmlDoc = XDocument.Load(path);
bool doesexists = (from data in xmlDoc.Element("list").Elements("channel")
where (string)data.Attribute("id") == channelname
select data).Any();
return doesexists;
}
but it's giving me the following error and I can't figure it out.. What am I doing wrong?
An unhandled exception of type 'System.Xml.XPath.XPathException' occurred in System.Xml.dll
Additional information: 'list/channel[#id='channelname1'' has an invalid token.
from this line
XmlNode node = root.SelectSingleNode("list/channel[#id='channelname1'");
My XML looks like this
<?xml version="1.0" encoding="UTF-8"?>
<list info="list">
<channel id="channelname1">
<display-name lang="en">channelname1</display-name>
<icon src="http://locationtologo.com/" />
<url>http://someurl.com</url>
</channel>
<channel id="channelname2">
<display-name lang="en">channelname2</display-name>
<icon src="http://locationtologo.com/" />
<url>http://someurl.com</url>
</channel>
<channel id="channelname3">
<display-name lang="en">channelname3</display-name>
<icon src="http://locationtologo.com/" />
<url>http://someurl.com</url>
</channel>
<channel id="channelname4">
<display-name lang="en">channelname4</display-name>
<icon src="http://locationtologo.com/" />
<url>http://someurl.com</url>
</channel>
</list>
You dont have closing bracket in list/channel[#id='channelname1'(HERE).
Moreover, you are actually trying to do following:
var doc = new XmlDocument();
doc.Load(Environment.CurrentDirectory + "\\channels.xml");
var nodes = doc.SelectNodes("list/channel[#id=\"channelname1\"]");
if (nodes != null)
{
foreach (XmlNode node in nodes)
{
var el = doc.CreateElement("channel-category");
el.InnerText = "SomeValue";
node.AppendChild(el);
}
}
bool doesexists = (from data in xmlDoc.Element("tv").Elements("channel")
where (string)data.Attribute("id") == channelname
select data).Any();
You are trying to reach the channel node where the id equals the channelname inside tv. The problem is that tv does not exist, the channels are inside this:
<list info="list">
Solution: either put the channels into tv, or use a selector appropriate to your current structure.
Why you are using tv instead of list thats why xml library not getting your path of your elements and throwing this error.
try this..
XmlNode node = root.SelectSingleNode("list/channel");
node.Attributes["id"].Value=="channelname1"?true:false;

Reading xml file on some condition

I want to use a xml file which is as below
<?xml version="1.0" encoding="utf-8" ?>
<pages>
<page name="Default.aspx">
<method name="Login_click">
<message code="0" description="this is a test description">
<client code="0000000000" description="this is a description for clent 0000000000" />
</message>
</method>
</page>
</pages>
Now I want to create a function like below
public static string GetAppMessage(string pageName, string methodName, string clientCode, string code)
{
var xmlFile = HttpContext.Current.Server.MapPath("~/App_Data/Theme.xml");
var doc = new XmlDocument();
doc.Load(xmlFile);
if (string.IsNullOrEmpty(clientCode))
{
//go to node who page name to pageName
//and read the vlue
}
else
{
//read for a particular client code
}
}
How can I do this.
Edit 1
Do I need to loop through each node or can I reach to a particular node directly and find the decedent nodes.
like below
foreach (XmlNode chldNode in doc.ChildNodes)
{
....
Instead of XmlDocument you can use XDocument and LINQ to xml:
var xmlFile = HttpContext.Current.Server.MapPath("~/App_Data/Theme.xml");
XDocument xmlDoc = XDocument.Load(xmlFile);
var xmlPage = (from page in xmlDoc.Descendants()
where page.Name.LocalName == "page"
&& page.Attribute("name").Value == pageName
select page).FirstOrDefault();
if (xmlPage != null)
{
//do what you need
}
When you use XmlDocument and you know how the XML-file will look like (I mean you know the names of the nodes where the information is inside) then you could do something like this:
XmlDocument doc = new XmlDocument();
doc.Load(path);
XmlElement root = doc["NameOfRootNode"];
if (root != null)
{
//For nodes you just need to bypass to get to another subnode:
XmlNode node = root.SelectSingleNode("nameOfAnotherNode");
//For nodes you actually want to do something with, like read text, attribute etc.
if (node != null && node.SelectSingleNode("nameOfOneMoreNode") != null)
{
var xmlElement = node["nameOfOneMoreNode"];
if (xmlElement != null)
{
//Read from the xmlElement you selected and do something with it...
}
}
//...
}
With SelectSingleNode or SelectNodes you can maneuver to a specific known node and can read the InnerText or an Attribute.
You can use XPath to get <page> element by it's name attribute, for example :
string xpath = "//page[#name='{0}']";
XmlNode page = doc.SelectSingleNode(string.Format(xpath, pageName));
//and read the vlue
Basically, above XPath look for <page> element having name attribute equals pageName parameter.

How to get the value for multiple subnode in xml?

Xml code:
<Report>
<ChartData>
<ListName>area</ListName>
<ViewName>Selecte List</ViewName>
<YAxisFields>
<YAxisField>
<Name>Scheduled Start Date/Time</Name>
<DataType>DateTime</DataType>
<Category>Year</Category>
</YAxisField>
</YAxisFields>
<XAxisFields>
<XAxisField>
<Name>Release Type</Name>
<DataType>String</DataType>
<Category>
</Category>
</XAxisField>
</XAxisFields>
</ChartConfig>
</Report>
I got the value for the subnode listname and viewname by using the
below code,
XmlDocument doc = new XmlDocument();
doc.Load("XmlFileName");
XmlNodeList node = doc.SelectNodes("Report/ChartData");
foreach (XmlNode xn in node)
{ xn["ListName"].InnerXml = chartname;
xn["ViewName"].InnerXml = SelectedList;
**xn["YAxisFields/YAxisField"].InnerXml = yaxisfield; //not working, need to get the value for this xml node,need help in this line dono how to proceed**
doc.Save("XmlFilename");
}
First i have tried with code like this instead of above code,in this
i need to create number of objects in order get the value for each
node so i tried by creating object for xmlnodelist then i used
foreach loop to get the value for each node but in this couldnt get
the value for YAxisFields/YAxisField because it also has parent node
as YAxisFields and subnode as YAxisField so there is only way to
create number of objects for xmlnode or is there any other way to do
this?
XmlDocument doc = new XmlDocument();
doc.Load("XmlFileName");
XmlNode Listnode = doc.SelectSingleNode("Report/ChartData/ListName");
XmlNode Viewnode = doc.SelectSingleNode("Report/ChartData/ViewName");
if (Listnode != null)
{
Listnode.InnerXml = chartname;
Viewnode.InnerXml = SelectedList; ;
doc.Save("XmlFileName");
Use Linq to XML XDocument, like this:
doc.Root.Descendants("ChartData").ToList().ForEach(node =>
{
node.Element("ListName").Value = chartname;
node.Element("ViewName").Value = SelectedList;
});

Parsing XmlDocument with Variable Parent Tags

Assuming I have an XmlDocument like this:
<xmlFile>
<details>
<code1>ADJ</code1>
<code2>ADC </code2>
<Shipment>
<foo></foo>
<bar></bar>
</Shipment>
<Shipment>
<foo></foo>
<bar></bar>
</Shipment>
</details>
<details>
<code1>ADJ</code1>
<code2>SCC </code2>
<Shipment>
<foo></foo>
<bar></bar>
</Shipment>
</details>
</xmlFile>
I need to process each in an xml file but only shipments that fall under the tags with a child node with a value of "ADC". So far I have:
// Assume there is an XmlDocument named xml
XmlNodeList details= xml.GetElementsByTagName("details");
foreach (XmlNode node in details)
{
if (node["code2"].InnerText == "ADC ")
{
// Get each shipment and process it accordingly.
}
}
I can't figure out what to do next. Thanks.
Assuming Data\Sample.xml contains xml as mentioned in the question,
Following is the XLINQ query
XElement root = XElement.Parse(File.ReadAllText(#"Data\Sample.xml"));
var adcShipment = root.Descendants().Where(e=>String.Equals(e.Value, "ADC "));
//next query for nodes/elements inside/next to ADC shipments
XPath can simplify your search for matches:
foreach (XmlNode node in xml.SelectNodes("/xmlFile/details[normalize-space(code2)='ADC']"))
{
string foo = node.SelectSingleNode("foo").InnerText;
string bar = node.SelectSingleNode("bar").InnerText;
}
I'm in the process of adding XPath parsing to this library: https://github.com/ChuckSavage/XmlLib/
I modified it so you can do this:
XElement root = XElement.Load(file);
var shipments = root.XPath("details[starts-with(*,'ADC')]/Shipment");
Long-hand that looks like:
var shipments = root.Elements("details")
.Where(x => x.Elements().Any(xx => ((string)xx).StartsWith("ADC")))
.Elements("Shipment");
This is the sort of thing you're after
XmlNodeList details = xml.GetElementsByTagName("details");
foreach (XmlNode node in details)
{
if (node["code2"].InnerText.Trim() == "ADC")
{
// Get each shipment and process it accordingly.
foreach(XmlNode shipment in node.SelectNodes("Shipment"))
{
var foo = shipment.SelectSingleNode("foo");
var bar = shipment.SelectSingleNode("bar");
}
}
}

Categories

Resources