Reading XML file in C# with XpathNavigator - c#

I am trying to read the book.xml file provided as an example on the MSDN website.
<?xml version="1.0" encoding="utf-8" ?>
<bookstore>
<book genre="autobiography" publicationdate="1981-03-22" ISBN="1-861003-11-0">
<title>The Autobiography of Benjamin Franklin</title>
<author>
<first-name>Benjamin</first-name>
<last-name>Franklin</last-name>
</author>
<price>8.99</price>
</book>
<book genre="novel" publicationdate="1967-11-17" ISBN="0-201-63361-2">
<title>The Confidence Man</title>
<author>
<first-name>Herman</first-name>
<last-name>Melville</last-name>
</author>
<price>11.99</price>
</book>
<book genre="philosophy" publicationdate="1991-02-15" ISBN="1-861001-57-6">
<title>The Gorgias</title>
<author>
<name>Plato</name>
</author>
<price>9.99</price>
</book>
</bookstore>
I have the following code until now:
static void Main()
{
XmlDocument document = new XmlDocument();
document.Load(#"c:\books.xml");
XPathNavigator navigator = document.CreateNavigator();
XPathNodeIterator nodes = navigator.Select("/bookstore/book");
while (nodes.MoveNext())
{
Console.WriteLine(nodes.Current.HasAttributes);
}
}
It seems this code is reading everything, but from here on if I want to display, say, just the titles of all book etc., how do I access them?

You can iterate over the titles if you change the XPath expression to select all title nodes:
XPathDocument document = new XPathDocument(#"c:\tmp\smpl5.xml");
XPathNavigator navigator = document.CreateNavigator();
XPathNodeIterator nodes = navigator.Select("/bookstore/book/title");
foreach (XPathNavigator item in nodes)
{
Console.WriteLine(item.Value);
}
Note that you don't need to create an XmlDocument if you don't plan to modify the document. Using an XPathDocument is usually more light-weight.

you can also use this "//title" instead of "/bookstore/book"

Related

printing list of xml nodes in c#

I am need bit of help on getting list of xml nodes and printing them.
My code is as below:
XmlDocument doc = new XmlDocument();
doc.Load("To44532.xml");
XmlNode xn = doc.DocumentElement;
foreach (XmlNode xn2 in xn)
{ Console.WriteLine(xn2); }
Console.ReadLine();
I am new to c# please accept my apologies in advance for asking this basic question. So I wanted full list of nodes and then printing them in output.
I ended up with this piece of code because I wanted to debug one of the other code. The idea was that I wanted to display specific nodes in winforms. I tried if statement e.g. :
foreach (XmlNode node in doc.DocumentElement)
{
if (node.Equals("DbtItm"))
{ ..... }
Could you please advise whats the best way to achieve it?
You can select XML Nodes by Name.
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="children">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="web">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
For example to get only book author and book year from as above xml.
XmlDocument xml = new XmlDocument();
xml.Load("XMLFile1.xml");
XmlNodeList xnList = xml.SelectNodes("/bookstore/book");
foreach (XmlNode xn in xnList)
{
string author = xn["author"].InnerText;
string year = xn["year"].InnerText;
Console.WriteLine(author+"-"+year);
}

Removing parent node from xml collections

I am trying to get rid of the parent node of a collection in xml (using linq-to-xml):
For example, the input xml:
<envelop>
<books>
<book>
<title>ABC</title>
<publishedDate>2012-12-12</publishedDate>
<authors>
<author>John Smith</author>
<author>Bob Doe</author>
</authors>
<book>
<book>
<title>XYZ</title>
<publishedDate>2013-03-06</publishedDate>
<authors>
<author>Henry Blah</author>
<author>Bob Doe</author>
</authors>
<book>
</books>
</envelop>
I would like the result to be:
<envelop>
<book>
<title>ABC</title>
<publishedDate>2012-12-12</publishedDate>
<author>John Smith</author>
<author>Bob Doe</author>
<book>
<book>
<title>XYZ</title>
<publishedDate>2013-03-06</publishedDate>
<author>Henry Blah</author>
<author>Bob Doe</author>
<book>
</envelop>
The nodes: <books> and <authors> are gone - only their children remain.
I saw the following question but with XSLT (Remove parent nodes from xml with xslt). Would like to stay away from XSLT at the moment.
var document = XDocument.Load(validXmlFilePath);
while (document.Descendants("authors").Any())
{
var x = document.Descendants("authors").First();
x.AddAfterSelf(x.Nodes());
x.Remove();
}
var result = document.Descendants("book");
Do you fill your object in code and than serialize it? If so, you can use [XmlElement("Book")] attribute above your list property in your class:
[XmlElement("Book")]
public List<Book> Books {get; set;}
In VB the code would be
Dim result As XElement = <envelop></envelop>
result.Add(xe...<book>)
For Each el As XElement In result...<author>.ToList
el.Parent.Parent.Add(New XElement(el))
Next
result...<authors>.Remove()
with xe defined as
Dim xe As XElement
'to load from a file
' xe = XElement.Load("Your Path Here")
' for testing
xe = <envelop>
<books>
<book>
<title>ABC</title>
<publishedDate>2012-12-12</publishedDate>
<authors>
<author>John Smith</author>
<author>Bob Doe</author>
</authors>
</book>
<book>
<title>XYZ</title>
<publishedDate>2013-03-06</publishedDate>
<authors>
<author>Henry Blah</author>
<author>Bob Doe</author>
</authors>
</book>
</books>
</envelop>
result looks like
<envelop>
<book>
<title>ABC</title>
<publishedDate>2012-12-12</publishedDate>
<author>John Smith</author>
<author>Bob Doe</author>
</book>
<book>
<title>XYZ</title>
<publishedDate>2013-03-06</publishedDate>
<author>Henry Blah</author>
<author>Bob Doe</author>
</book>
</envelop>

return the same XML Doc structure with specific conditions in XPath

I have xml document similar to xml in page http://www.w3schools.com/xml/xml_xpath.asp
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="children">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="web">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2003</year>
<price>49.99</price>
</book>
<book category="web">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
the problem is how to return this document with elements corresponding to a certain value? how to XPath or XQuery command should be written ?
for example search for title contains 'Learning' then the return xml doc should be like:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book category="web">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
how to get this result?
another question how to search with ignoring characters case, so 'learNING' should return the same result?
Using XQuery you can do as follow :
<bookstore>
{
for $d in //book[contains(lower-case(title),'learning')]
return $d
}
</bookstore>
Xpathtester Demo
Notice that there will be only one <bookstore> that wraps all matched <book> elements returned, and notice the use of lower-case() function to 'ignore' character case of the book title in the matching process.
Use XML Linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<XElement> book = doc.Descendants("book").Where(x => x.Element("title").Value.Contains("Learn")).ToList();
XDocument filteredDoc = XDocument.Parse("<?xml version=\"1.0\" encoding=\"UTF-8\"?><bookstore></bookstore>");
XElement root = (XElement)filteredDoc.FirstNode;
root.Add(book);
}
}
}​
for $d in doc('books')//book[title[contains(text(),'Beginning')]]
return <bookstore> {$d} </bookstore>
However this solution does not handle ignoring character case.

Replace element in xdocument placed in a treeview with another xelement

I have a simple xmlfile and that is placed in a treeview
<bookstore xmlns="generic">
<book genre="Book" >`
<title>Book of Benjamin Franklin</title>
<author>
<first-name>Benson</first-name>
<last-name>Frank</last-name>
</author>
<price>89.88</price>
</book>
<book genre="autobiography">
<title>The Autobiography of Benjamin Franklin</title>
<author>
<first-name>Ben</first-name>
<middlename />
<last-name>Franklin</last-name>
</author>
<price>89.88</price>
</book>
<book genre="novel" >
<title>The Confidence Man</title>
<author>
<first-name>John</first-name>
<last-name>Melville</last-name>
</author>
<price>11.99</price>
</book>
</bookstore>
I want to replace (2nd element)
<book genre="autobiography" >
<title>The Autobiography of Benjamin Franklin</title>
<author>
<first-name>Ben</first-name>
<middlename />
<last-name>Franklin</last-name>
</author>
<price>89.88</price>
</book>
with an another element(that is edited and i have edited portion as a string)
<book genre="autobiography">
<title>The Autobiography of Benjamin Franklin</title>
<author>
<first-name>Ben</first-name>
<last-name>Franklin</last-name>
</author>
<price>89.88</price>
</book>
Code I used is here
XElement XmlTree = XElement.Parse(text);//text contain edited portion
XmlElement XMLE = (XmlElement)TreeV.SelectedItem;
XmlDocument xdoc = new XmlDocument();
xdoc.Load(scd_file);
XmlDocumentFragment xfrag = xdoc.CreateDocumentFragment();
xfrag.InnerXml = text;
XmlDocumentFragment xfrag1 = xdoc.CreateDocumentFragment();
xfrag1.InnerXml = XMLE.OuterXml;
xdoc.ReplaceChild(xfrag, xfrag1);
But it shows error (xfrag1 is not a node of xdoc)
please help me to solve this problem.
thank u all i got the solution.
XmlElement XMLE = (XmlElement)TreeV.SelectedItem;// selection from treeview(TreeV) that we want to edit (2nd element)
XmlDocument XD = new XmlDocument();
XD.LoadXml(text);// text is edited part save as a string
XmlNode root=XD.DocumentElement;
XmlDocument xml = XMLE.ParentNode.OwnerDocument;
XmlNode import= xml.ImportNode(root, true);
XMLE.ParentNode.ReplaceChild(import, XMLE);
xml.Save(scd_file); //scd_file path

Get XML content from XmlNodeList

I have a question that may seem very simple, but it's giving me a headache. I have this XML file that has multiple entries, like:
<books>
<book>
<id>1</id>
<firstCover>
<author name="**" age="**" />
<title name="zz" font="yyy" size="uuu"/>
</firstCover>
<lastCover>
</lastCover>
</book>
<book>
<id>2</id>
<firstCover>
<author name="**" age="**" />
<title name="zz" font="yyy" size="uuu"/>
</firstCover>
<lastCover>
</lastCover>
</book>
</books>
Now, in order to get the XML content for first cover of book with id=1, I do this:
XmlNodeList b = root.SelectNodes("/books/book[contains(id,1)]/firstCover");
Then I would really need to take the whole content of what's inside the firstCover for that book :
<author name="**" age="**" />
<title name="zz" font="yyy" size="uuu"/>
and insert it into an XmlElement. This is where I'm stucked. I know I can do it with a foreach loop in XmlNodeList, but is there a more simple way?
I'm guessing you want to actually insert it into an XMLElement in another XMLDocument.
Is this what you are looking for?
XmlDocument sourceDoc = new XmlDocument();
//This is loading the XML you present in your Question.
sourceDoc.LoadXml(xmlcopier.Properties.Resources.data);
XmlElement root = sourceDoc.DocumentElement;
XmlElement b = (XmlElement)root.SelectSingleNode("/books/book[contains(id,1)]/firstCover");
XmlDocument destDoc = new XmlDocument();
XmlElement destRoot = destDoc.CreateElement("base");
destDoc.AppendChild(destRoot);
XmlElement result = destDoc.CreateElement("firstCover");
result.InnerXml = b.InnerXml;
destRoot.AppendChild(result);
destDoc.Save("c:\\test.xml");

Categories

Resources