reading node from xml file in XMLDocument - c#

i am trying to grab the TopicName how should i go after it and try different combination but somehow i am unable to get TopicName below is my source codee...
XmlDocument xdoc = new XmlDocument();//xml doc used for xml parsing
xdoc.Load(
"http://latestpackagingnews.blogspot.com/feeds/posts/default"
);//loading XML in xml doc
XmlNodeList xNodelst = xdoc.DocumentElement.SelectNodes("content");//reading node so that we can traverse thorugh the XML
foreach (XmlNode xNode in xNodelst)//traversing XML
{
//litFeed.Text += "read";
}
sample xml file
<content type="application/xml">
<CatalogItems xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="sitename.xsd">
<CatalogSource Acronym="ABC" OrganizationName="ABC Corporation" />
<CatalogItem Id="3212" CatalogUrl="urlname">
<ContentItem xmlns:content="sitename.xsd" TargetUrl="url">
<content:SelectionSpec ClassList="" ElementList="" />
<content:Language Value="eng" Scheme="ISO 639-2" />
<content:Source Acronym="ABC" OrganizationName="ABC Corporation" />
<content:Topics Scheme="ABC">
<content:Topic TopicName="Marketing" />
<content:Topic TopiccName="Coverage" />
</content:Topics>
</ContentItem>
</CatalogItem>
</CatalogItems>
</content>

The Topic nodes in your XML are using the content namespace - you need to declare and use the XML namespace in your code, then you can use SelectNodes() to grab the nodes of interest - this worked for me:
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xdoc.NameTable);
nsmgr.AddNamespace("content", "sitename.xsd");
var topicNodes = xdoc.SelectNodes("//content:Topic", nsmgr);
foreach (XmlNode node in topicNodes)
{
string topic = node.Attributes["TopicName"].Value;
}
Just as a comparison see how easy this would be with Linq to XML:
XDocument xdoc = XDocument.Load("test.xml");
XNamespace ns = "sitename.xsd";
string topic = xdoc.Descendants(ns + "Topic")
.Select(x => (string)x.Attribute("TopicName"))
.FirstOrDefault();
To get all topics you can replace the last statement with:
var topics = xdoc.Descendants(ns + "Topic")
.Select(x => (string)x.Attribute("TopicName"))
.ToList();

If you just need a specific element, then I'd use XPath:
This is a guide to use XPath in C#:
http://www.codeproject.com/KB/XML/usingXPathNavigator.aspx
And this is the query that will get you a collection of your Topics:
//content/CatalogItems/CatalogItem/ContentItem/content:Topics/content:Topic
You could tweak this query depending on what it is you're trying to accomplish, grabbing just a specific TopicName value:
//content/CatalogItems/CatalogItem/ContentItem/content:Topics/content:Topic/#TopicName
XPath is pretty easy to learn. I've done stuff like this pretty quickly with no prior knowledge.
You can paste you XML and xpath query here to test your queries:
http://www.bit-101.com/xpath/

The following quick and dirty LINQ to XML code obtains your TopicNames and prints them on the console.
XDocument lDoc = XDocument.Load(lXmlDocUri);
foreach (var lElement in lDoc.Element("content").Element(XName.Get("CatalogItems", "sitename.xsd")).Elements(XName.Get("CatalogItem", "sitename.xsd")))
{
foreach (var lContentTopic in lElement.Element(XName.Get("ContentItem", "sitename.xsd")).Element(XName.Get("Topics", "sitename.xsd")).Elements(XName.Get("Topic", "sitename.xsd")))
{
string lTitle = lContentTopic.Attribute("TopicName").Value;
Console.WriteLine(lTitle);
}
}
It'd have been a lot shorter if it wasn't for all the namespaces :) (Instead of "XName.Get" you would just use the name of the element).

Related

Parsing xml response - Select specific node based on value of another node

I'm new to xml so I'm not sure if I worded the question correctly, but I will do my best to explain.
Basically, I'm trying to parse an xml response in C# such as the one below:
<Premium>
<TotalPremiumAmount>87</TotalPremiumAmount>
<StandardPremium>87</StandardPremium>
<OptionalPremium>0</OptionalPremium>
<StandardTax>0</StandardTax>
<OptionalTax>0</OptionalTax>
<ExtendedTax>0</ExtendedTax>
<ExtendedPremium>0</ExtendedPremium>
<PromotionalPremium>0</PromotionalPremium>
<FeesPremium>0</FeesPremium>
<FeesTax>0</FeesTax>
<StandardFeesPremium>0</StandardFeesPremium>
<OptionalFeesPremium>0</OptionalFeesPremium>
<Tax>0</Tax>
<StandardPremiumDistribution>
<Travelers>
<Traveler>
<TravelerPremium>42</TravelerPremium>
<TravelerTax>0</TravelerTax>
</Traveler>
<Traveler>
<TravelerPremium>45</TravelerPremium>
<TravelerTax>0</TravelerTax>
</Traveler>
</Travelers>
</StandardPremiumDistribution>
<PackagePremiumDistribution>
<Packages>
<Package>
<PackageID>20265</PackageID>
<PackageName />
<PackageTypeID>12</PackageTypeID>
<Premium>87</Premium>
<Fees>0</Fees>
<Tax>0</Tax>
<Travelers>
<Traveler>
<TravelerID>0</TravelerID>
<Premium>42</Premium>
<Tax>0</Tax>
</Traveler>
<Traveler>
<TravelerID>1</TravelerID>
<Premium>45</Premium>
<Tax>0</Tax>
</Traveler>
</Travelers>
</Package>
</Packages>
</PackagePremiumDistribution>
</Premium>
I would like to get the value of the (Traveler) Premium. In the case of only one traveler, I have been using an XMLDocument and the 'SelectSingleNode" function. For example I could do something like:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlResponse);
var premium = xmlDoc.SelectSingleNode("//TravelerPremium").InnerText;
But this wouldn't work when multiple travelers are returned under one plan. For example, I need the premium when TravelerID = 0. How would I go about doing this?
Thanks.
Using XmlDocument:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlResponse);
var premium = xmlDoc.SelectSingleNode("//Premium[../TravelerID = '0']")
You could also iterate through the nodes if multiple could match on this condition like so:
foreach(var premium in xmldoc.SelectNodes("//Premium[../TravelerID = '0']")
{
// do work on each premium node where TravelerID = 0
}
I'd encourage you to look into using LINQ to XML - it's generally easier to work with and will be more performant in most cases. You could even still use XPath expressions, but the following would work:
XDocument xdoc = XDocument.Load(xmlResponse);
var premium = (string)xdoc.Descendants("Traveler").Where(x => (string)x.Element("TravelerID") == "0").Element("Premium");
Assuming your xml looks like that, try something like this:
XmlDocument doc = new XmlDocument();
xmlDoc.Load(xmlResponse);
if (doc.HasChildNodes)
{
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
if (node.Name == "StandardPremiumDistribution")
{
XmlNodeList xnList = node.SelectNodes("//Travelers");
double travelerPremium= xnList.Item(z).FirstChild.InnerText);
}}
Based on this, I think you're gonna do it.
Let's suppose you have a file called XMLFile1.xml with the XML you posted you can iterate through all your TravelerPremium with the following code:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("XMLFile1.xml");
XmlNodeList premiums = xmlDoc.SelectNodes("//TravelerPremium");
foreach(XmlNode node in premiums)
{
MessageBox.Show(node.FirstChild.InnerText);
}
You can also acces the other elements with similar code.

Working With Specific XML structure

I am trying to get some data from an XML document. I have no control over the schema. If it were up to me I would have chosen another schema. I am using C#'s XPATH library to get the data.
XML DOC
<Journals>
<name>Title of Journal</name>
<totalvolume>2</totalvolume>
<JournalList>
<Volume no="1">
<Journal>
<issue>01</issue>
<Title>Title 1</Title>
<date>1997-03-10</date>
<link>www.somelink.com</link>
</Journal>
<Journal>
<issue>02</issue>
<Title>Title 3</Title>
<date>1997-03-17</date>
<link>www.somelink.com</link>
</Journal>
</Volume>
<Volume no="2">
<Journal>
<issue>01</issue>
<Title>Title 1</Title>
<date>1999-01-01</date>
<link>www.somelink.com</link>
</Journal>
<Journal>
<issue>01</issue>
<Title>Title 2</Title>
<date>1999-01-08</date>
<link>www.somelink.com</link>
</Journal>
</Volume>
</JournalList>
</Journals>
I am trying to get all the data in the Volume 2 node. Here is what I tried so far:
C# Code:
protected void loadXML(string url)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(url);
string strQuery = "Volume[#no='2']";
XmlElement nodeList = xmlDoc.DocumentElement;
XmlNodeList JournalList = nodeList.SelectNodes(strQuery);
foreach (XmlElement Journal in JournalList)
{
XmlElement temp = Journal;
}
}
It seems there are no nodes in JournalList. Anyone? Thanks in advance/
Your code is looking for "Volume" nodes directly under the "Journals" node
Change this:
string strQuery = "Volume[#no='2']";
To this, in order to look for "Volume" nodes under the "JournalList" node:
string strQuery = "JournalList/Volume[#no='2']";
Also, there's a couple typos in your XML:
</Volume no="2"> -> <Volume no="2"> // no open tag found
</Journal> -> </Journals> // expecting end tag </Journals>
From your comment below:
how would I go about access each journal? for example. I want irrate through each "journal" and get the title of the journal?
In order to do that, you could modify your code slightly:
var nodeList = xmlDoc.DocumentElement;
var volume = nodeList.SelectSingleNode(strQuery);
foreach (XmlElement journal in volume.SelectNodes("Journal"))
{
var title = journal.GetElementsByTagName("Title")[0].InnerText;
}
Also you can use Linq to XML:
using System.Xml.Linq;
//...
string path="Path of your xml file"
XDocument doc = XDocument.Load(path);
var volume2= doc.Descendants("Volume").FirstOrDefault(e => e.Attribute("no").Value == "2");

Getting an XElement with a namespace via XPathSelectElements

I have an XML e.g.
<?xml version="1.0" encoding="utf-8"?>
<A1>
<B2>
<C3 id="1">
<D7>
<E5 id="abc" />
</D7>
<D4 id="1">
<E5 id="abc" />
</D4>
<D4 id="2">
<E5 id="abc" />
</D4>
</C3>
</B2>
</A1>
This is may sample code:
var xDoc = XDocument.Load("Test.xml");
string xPath = "//B2/C3/D4";
//or string xPath = "//B2/C3/D4[#id='1']";
var eleList = xDoc.XPathSelectElements(xPath).ToList();
foreach (var xElement in eleList)
{
Console.WriteLine(xElement);
}
It works perfectly, but if I add a namespace to the root node A1, this code doesn't work.
Upon searching for solutions, I found this one, but it uses the Descendants() method to query the XML. From my understanding, this solution would fail if I was searching for <E5> because the same tag exists for <D7>, <D4 id="1"> and <D4 id="2">
My requirement is to search if a node exists at a particular XPath. If there is a way of doing this using Descendants, I'd be delighted to use it. If not, please guide me on how to search using the name space.
My apologies in case this is a duplicate.
To keep using XPath, you can use something link this:
var xDoc = XDocument.Parse(#"<?xml version='1.0' encoding='utf-8'?>
<A1 xmlns='urn:sample'>
<B2>
<C3 id='1'>
<D7><E5 id='abc' /></D7>
<D4 id='1'><E5 id='abc' /></D4>
<D4 id='2'><E5 id='abc' /></D4>
</C3>
</B2>
</A1>");
// Notice this
XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
nsmgr.AddNamespace("sample", "urn:sample");
string xPath = "//sample:B2/sample:C3/sample:D4";
var eleList = xDoc.XPathSelectElements(xPath, nsmgr).ToList();
foreach (var xElement in eleList)
{
Console.WriteLine(xElement);
}
but it uses the Descendants() method to query the XML. From my understanding, this solution would fail if I was searching for because the same tag exists for , and
I'm pretty sure you're not quite understanding how that works. From the MSDN documentation:
Returns a filtered collection of the descendant elements for this document or element, in document order. Only elements that have a matching XName are included in the collection.
So in your case, just do this:
xDoc.RootNode
.Descendants("E5")
.Where(n => n.Parent.Name.LocalName == "B4");
Try this
var xDoc = XDocument.Parse("<A1><B2><C3 id=\"1\"><D7><E5 id=\"abc\" /></D7><D4 id=\"1\"><E5 id=\"abc\" /></D4><D4 id=\"2\"><E5 id=\"abc\" /></D4></C3></B2></A1>");
foreach (XElement item in xDoc.Element("A1").Elements("B2").Elements("C3").Elements("D4"))
{
Console.WriteLine(item.Element("E5").Value);//to get the value of E5
Console.WriteLine(item.Element("E5").Attribute("id").Value);//to get the value of attribute
}

Linq-to-Xml prefix parse error

I am currently trying to learn how to parse data, and its a bit confusing. can someone check out my code and see what I'm doing wrong or if im even heading in the right direction.
XML File:
<xml xmlns:a='BLAH'
xmlns:b='BLAH'
xmlns:c='BLAH'
xmlns:d='BLAH'>
<a:info>
<b:cat Option1='blah' Option2='blah' Option3='blah' />
</a:info>
</xml>
C# Code:
XmlDocument doc = new XmlDocument();
doc.Load(richTextBox2.Text);
XmlNamespaceManager man = new XmlNamespaceManager(doc.NameTable);
man.AddNamespace("a", "BLAH");
man.AddNamespace("b", "BLAH");
man.AddNamespace("c", "BLAH");
man.AddNamespace("d", "BLAH");
XmlNode temps = doc.SelectSingleNode("/a:info/b:cat/Option1/", man);
richTextBox1.Text = temps.InnerText;
I am new to C#, I cant find a good example explaining how to successfully use loops to find more then one:
<b:chat />
You are using the wrong API if you're looking for LINQ to XML. Use the XDocument class instead.
Assume the following input XML-document (please note namespace URLs):
<xml xmlns:a='http://localhost/scheme_a'
xmlns:b='http://localhost/scheme_b'
xmlns:c='http://localhost/scheme_c'
xmlns:d='http://localhost/scheme_d'>
<a:info>
<b:cat Option1='1' Option2='1' Option3='1' />
</a:info>
<a:info>
<b:cat Option1='2' Option2='2' Option3='2' />
</a:info>
</xml>
There are the ways to get all <b:chat /> elements.
XmlDocument class:
var xmlDocument = new XmlDocument();
xmlDocument.Load(...);
var xmlNamespaceManager = new XmlNamespaceManager(xmlDocument.NameTable);
xmlNamespaceManager.AddNamespace("a", "http://localhost/scheme_a");
xmlNamespaceManager.AddNamespace("b", "http://localhost/scheme_b");
xmlNamespaceManager.AddNamespace("c", "http://localhost/scheme_c");
xmlNamespaceManager.AddNamespace("d", "http://localhost/scheme_d");
var bCatNodes = xmlDocument.SelectNodes("/xml/a:info/b:cat", xmlNamespaceManager);
var option1Attributes = bCatNodes.Cast<XmlNode>().Select(node => node.Attributes["Option1"]);
// Also, all Option1 attributes can be retrieved directly using XPath:
// var option1Attributes = xmlDocument.SelectNodes("/xml/a:info/b:cat/#Option1", xmlNamespaceManager).Cast<XmlAttribute>();
LINQ to XML XDocument Class. XName can be passed with a namespace to Descendants() and Element() methods.
Use Descendants() to get all <b:chat /> elements.
var xDocument = XDocument.Load(...);
XNamespace xNamespace = "http://localhost/scheme_b";
var xElements = xDocument.Descendants(xNamespace + "cat");
// For example, get all the values of Option1 attribute for the b:chat elements:
var options1 = xElements.Select(element => element.Attribute("Option1")).ToList();

Search XML file for nodes with specific attribute value in .NET 2

I found answers for searching XML nodes using LINQ, but I am limited to C# with .NET 2.
I want to open a single XML file (~50Kb, all simple text) and search for all <Tool> nodes with attribute name having a specific value.
It seems like XmlDocument.SelectNodes() might be what I'm looking for, but I don't know XPath. Is this the right way and if so what would code look like?
You can use XPath in XmlDocument.SelectNodes such as: SelectNodes("//ElementName[#AttributeName='AttributeValue']")
Xml Sample:
<root>
<element name="value1" />
<element name="value2" />
<element name="value1" />
</root>
C# Sample:
XmlDocument xDoc = new XmlDocument();
// Load Xml
XmlNodeList nodes = xDoc.SelectNodes("//element[#name='value1']");
// nodes.Count == 2
Here you can find some additional XPath samples
think you could do something like that (well, rustic, but you've got the idea), using GetElementsByTagName
var myDocument = new XmlDocument();
myDocument.Load(<pathToYourFile>);
var nodes = myDocument.GetElementsByTagName("Tool");
var resultNodes = new List<XmlNode>();
foreach (XmlNode node in nodes)
{
if (node.Attributes != null && node.Attributes["name"] != null && node.Attributes["name"].Value == "asdf")
resultNodes.Add(node);
}

Categories