Reading XML using XmlDocument - c#

This is my feed.
<feed xml:lang="">
<title>NEWS.com.au | Top Stories</title>
<link rel="self" href="http://feeds.news.com.au/public/atom/1.0/news_top_stories_48_48.xml"/>
<link rel="alternate" href="http://news.com.au"/>
<id>http://news.com.au</id>
<rights/>
<entry>
<title>F1’s glaring issues exposed</title>
<link href="www.google.com"/>
<author>
<name>STEVE LARKIN</name>
</author>
<link rel="enclosure" type="image/jpeg" length="2373" href="abc.jpg"/>
</entry>
<entry>
.....
</entry>
</feed>
This is how i am reading the xml.
string downloadfolder = "C:/Temp/Download/abc.xml";
XmlDocument xml = new XmlDocument();
xml.Load(downloadfolder);
XmlNamespaceManager nsmgr = new System.Xml.XmlNamespaceManager(xml.NameTable);
nsmgr.AddNamespace("atom", "http://www.w3.org/2005/Atom");
string xpath_title = "atom:feed/atom:entry/atom:title";
XmlNodeList nodes_title = xml.SelectNodes(xpath_title, nsmgr);
foreach (XmlNode node_title in nodes_title)
{
Console.WriteLine(node_title.InnerText);
}
string xpath_author = "atom:feed/atom:entry/atom:author";
XmlNodeList nodes_author = xml.SelectNodes(xpath_author, nsmgr);
foreach (XmlNode node_author in nodes_author)
{
Console.WriteLine(node_author.InnerText);
}
string xpath_link = "atom:feed/atom:entry/atom:link";
XmlNodeList nodes_link = xml.SelectNodes(xpath_link, nsmgr);
foreach (XmlNode node_link in nodes_link)
{
Console.WriteLine(node_link.Attributes["href"].Value);
}
I want to read title, link, author inside the <entry> node. i am defining xpath and then iterating the values of each node is there any other way to define xpath once and then iterate all the values from the <entry> node

To operate on all child nodes of <entry> node, you can stop your XPath at /atom:entry. Then inside the loop, select each child nodes as you want, for example :
......
String xpath = "atom:feed/atom:entry";
XmlNodeList nodes2 = xml.SelectNodes(xpath, nsmgr);
foreach (XmlNode node in nodes2)
{
var title = node.SelectSingleNode("./atom:title", nsmgr).InnerText;
var link1 = node.SelectSingleNode("./atom:link[1]", nsmgr).Attributes["href"].Value;
//go on to select and operate on the rest child nodes
//.......
}
Notice that you need to add a dot (.) at the beginning of the XPath to make XPath context relative to current node instead of the entire XML document.

To read the href attribute you will need to modify your xpath expression to...
string xpath = "atom:feed/atom:entry/atom:link";
this will iterate through all links within entries. Then, you will need read that specific attibute's value instead of reading the InnerText
Console.WriteLine(node.Attributes["href"].Value);
Now, if you want to read everything inside the entry elements, xpath will quickly get your code a bit messy. A cleaner solution, IMHO, would be using xml serialization so that you can easily parse/serialize "entries" into POCO objects. Then, you can do whatever you want with these objects

Related

Getting a whole XML node with its markups in a string

Given the following XML example
<aaa>
<bbb id="1">
<ccc att="123"/>
<ccc att="456"/>
<ccc att="789"/>
</bbb>
<bbb id="2">
<ccc att="321"/>
<ccc att="654"/>
<ccc att="987"/>
</bbb>
</aaa>
as an XmlDocument object called xDoc1, I managed to remove the first bbb node thanks to its ID and an XPath instruction, leaving the second bbb node alone in the aaa one.
But now I want to get this removed node and its markups in a single string, as the InnerText value of this node is equal to
<ccc att="123"/><ccc att="456"/><ccc att="789"/>
but I want my string to be equal to
<bbb id='1'><ccc att="123"/><ccc att="456"/><ccc att="789"/></bbb>
How can I do so ? Using XmlDocument is mandatory.
I tried to use the ParentNode method, but then it includes the other bbb node.
My C# code for the moment :
xDoc1 = new XmlDocument();
xDoc1.Load("file.xml"); // Containing the given example above.
XmlNodeList nodes = xDoc1.SelectSingleNodes("//bbb[#id='1']");
foreach (XmlNode n in nodes)
{
XmlNode parent = n.ParentNode;
parent.RemoveChild(n);
}
// At this point, xDoc1 does not contain the first bbb node (id='1') anymore.
Use OuterXml property of XmlNode
xDoc1 = new XmlDocument();
xDoc1.Load("file.xml"); // Containing the given example above.
XmlNodeList nodes = xDoc1.SelectSingleNodes("//bbb[#id='1']");
foreach (XmlNode n in nodes)
{
XmlNode parent = n.ParentNode;
parent.RemoveChild(n);
Console.WriteLine(n.OuterXml);
}
I would firstly suggest not using XmlDocument. It's old tech and has been superseded by XDocument, which gives you Linq2Xml and lots of explicit casting goodness when dealing with attributes etc.
Using the XDocument approach with Linq instead of XPath, it's quite a lot easier to work this problem:
var doc=XDocument.Load("file.xml");
var elToRemove = doc.Root.Elements("bbb").Single(el => (int)el.Attribute("id") == 1);
elToRemove.Remove();
Console.WriteLine(doc.ToString()); //no <bbb id="1">
Console.WriteLine(elToRemove.ToString()); //the full outer text of the removed <bbb>

how iterate through child element of multiple parents xml file c#

I have an xml file like this:
<post>
<categories>
<category ref="4527" />
<category ref="4528" />
<category ref="4529" />
<category ref="4530" />
<category ref="4531" />
</categories>
</post>
<post>
<categories>
<category ref="4523" />
<category ref="4524" />
<category ref="4525" />
<category ref="4526" />
<category ref="4527" />
</categories>
</post>
Using C# and .Net 4.5 I want to get the first set of category reference numbers, then process them, then move to the next set of category reference numbers and process them. I am hoping that some one can point me in the right direction. I am not sure how to do this using XPath or with Linq to XML or if those are even the right approach. Thanks in advance.
After some responses to some very smart people I was able to use Selman22's train of thought to help me write some XPath. Here is the solution I came up with:
XmlDocument xdoc = new XmlDocument;
xdoc.Load(savePath);
XmlNode root = xdoc.DocumentElement;
// add the namespace
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xdoc.NameTable);
nsmgr.AddNamespace("bml", "http://www.blogml.com/2006/09/BlogML");
//puts the catagories elements into a list
XmlNodeList blogCatagories = root.SelectNodes("descendant::bml:post/bml:categories", nsmgr);
//loop throught list and place the attribute "ref" into a list and traverse each "ref"
foreach (XmlNode nodeCat in blogCatagories)
{
XmlNodeList catagoryids = nodeCat.SelectNodes("descendant::bml:category/#ref", nsmgr);
foreach (XmlNode nodeID in catagoryids)
{
Console.WriteLine(nodeID.InnerText.ToString());
}
}
First get your categories
var xdDoc = XDocument.Load(path);
var categories = xDoc.Descendants("categories").ToList();
Then loop through your category list
foreach(var cat in categories)
{
var numbers = cat.Elements("category").Select(c => (int)c.Attribute("ref"));
foreach(var number in numbers)
{
// process your numbers
}
}
var xdoc = XDocument.Load(path_to_xml);
var query = from p in xdoc.Root.Descendants("post")
select p.Element("categories")
.Elements("category")
.Select(c => (int)c.Attribute("ref"))
.ToList();
This query will return iterator which will get next sequence of category reference numbers each time you are iterating it.
foreach(List<int> references in query)
{
// process list of references
foreach(int reference in references)
// process reference
}
XPathNavigator xml = new XPathDocument(filename).CreateNavigator();
foreach(XPathNavigator categories in xml.Select("//categories"))
{
foreach(XPathNavigator category in categories.Select("category"))
{
string category_ref = category.GetAttribute("ref", string.Empty);
}
// do processing
}
After some responses to some very smart people I was able to use Selman22's train of thought to help me write some XPath.
XmlDocument xdoc = new XmlDocument;
xdoc.Load(savePath);
XmlNode root = xdoc.DocumentElement;
// add the namespace
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xdoc.NameTable);
nsmgr.AddNamespace("bml", "http://www.blogml.com/2006/09/BlogML");
//puts the catagories elements into a list
XmlNodeList blogCatagories = root.SelectNodes("descendant::bml:post/bml:categories", nsmgr);
//loop throught list and place the attribute "ref" into a list and traverse each "ref"
foreach (XmlNode nodeCat in blogCatagories)
{
XmlNodeList catagoryids = nodeCat.SelectNodes("descendant::bml:category/#ref", nsmgr);
foreach (XmlNode nodeID in catagoryids)
{
Console.WriteLine(nodeID.InnerText.ToString());
}
}
I would use XPathDocument and XPathNavigator, lots of examples on google like this
http://www.codegod.com/XPathDocument-XPathNavigator-XPathNodeIterator-sample-with-C-AID504.aspx

inserting xml element into multiple nodes

I have the following XML
<ROOT>
<FSM338_Container>
<FSM338_Details>
<RunDate>2013-05-29 09:43:00</RunDate>
<Uic>21690</Uic>
<Date>2013-06-10 00:00:00</Date>
<CASHBREAK>199</CASHBREAK>
<CASHLUNCH>199</CASHLUNCH>
</FSM338_Details>
<FSM338_Details>
<RunDate>2013-05-29 09:43:00</RunDate>
<Uic>21690</Uic>
<Date>2013-06-10 00:00:00</Date>
<CASHBREAK>199</CASHBREAK>
<CASHLUNCH>199</CASHLUNCH>
</FSM338_Details>
</FSM338_Container>
<BillingReport>
<RunDate>2013-05-29 09:43:00</RunDate>
<Uic>21690</Uic>
<Date>2013-06-10 00:00:00</Date>
<gaindacd>1</gaindacd>
<docnum>07000F</docnum>
</BillingReport>
<DataElements>
<unitid>12345</unitid>
<fbocost>0.00</fbo>
</DataElements>
</ROOT>
I need to load the xml doc and add in several elements whenever I find the element named "Uic" . In short if I find "Uic" add in the element <someElement>my stuff here</someElement> at the same level as UIC at all locations.
I'Ve used
XmlDocument xDoc = new XmlDocument();
xDoc.Load(#"path_to_xml.xml");
list = xDoc.GetElementsByTagName("Uic");
I used insertBefore to add in my element but I can get it to copy to only the first element
You can use SelectNodes() method of XmlNode which accepts xpath expression.
XmlNodeList nodes = xDoc.DocumentElement.SelectNodes("Uic");
foreach(XmlNode node in nodes) {
XmlElement element = xDoc.CreateElement("SomeElement");
element.InnerText = "anything";
node.ParentNode.AppendChild(element);
}

Can't get to <style> in xml file using XmlDocument

trying to get to <style> to modify it, but I can't get any further than <layouts>, here is my code:
XmlDocument doc = new XmlDocument();
doc.Load(fi.FullName);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("rep", "http://developer.cognos.com/schemas/report/8.0/");
XmlNodeList nodeList = doc.SelectNodes("descendant::rep:layouts", nsmgr);
foreach (XmlNode node in nodeList)
{
Console.WriteLine(node.Name);
//XmlNode styleNode = node.SelectSingleNode("style");
//if (styleNode != null)
// Console.WriteLine(styleNode.InnerText);
}
So, this works ("descendant::rep:layouts"), I get to see (Console.WriteLine=) "layouts". But if I try to get further, even if one node at a time, (descendant::rep:layouts/layout/reportPages/page/pageBody/contents/crosstab/style), there is no single node in the list.. please help!!! My ultimate goal is to modify the "CSS style".
XML file is below (pasted from comment):
<report xmlns= developer.cognos.com/schemas/report/8.0/">
<queries>
<layouts> <layout> <reportPages>
<page name="Page1"> <pageBody> <contents>
<block> <contents> <block> <crosstab name="Crosstab1" refQuery="Query1">
<style>
<CSS value="border-collapse:collapse;font-family:'Times New Roman';border:0.75pt solid black" /> <defaultStyles>
</style>
Most likely all other nodes have some non-empty namespaces (note that empty prefix does not mean "no namespace"). One would need to see XML for better answer.
Yes all your nodes are have "how to ignore namespaces with XPath" set as default namespace, so no nodes have it as prefix. If you want to learn more - click on "xml-namespaces" tag for details. Otherwise use following to ignore namespaces how to ignore namespaces with XPath or prefix node names with namespace prefix as you've done for layouts one:
XmlNode styleNode = node.SelectSingleNode("rep:style", nsmgr);

reading node from xml file in XMLDocument

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).

Categories