c# code to read xml file values - c#

I have this xml file and I want to extract author name and access number and what I have is a very naive implementation in C# where I am using xml reader and reading line by line. But I am looking for an implementation where I can read the author name and access number in c# efficiently. I am new to C# and I have been told that LINQ should be used but looking at the document and this file I am not able to relate how to use Xdocument. Any help will be appreciated.
<xml>
<records>
<record>
<database name="CP_EndnoteLibrary_2012-2015-1.enl" path="C:\Users\Downloads\file.enl">file.enl</database>
<source-app name="EndNote" version="17.4">EndNote</source-app>
<rec-number>24</rec-number>
<contributors>
<authors>
<author>
<style face="normal" font="default" size="100%">ABCD, X.</style>
</author>
<author>
<style face="normal" font="default" size="100%">EFGH, I.</style>
</author>
</authors>
</contributors>
<accession-num>
<style face="normal" font="default" size="100%">12345678</style>
</accession-num>
</record>
<record>...</record>
</records>
Following a document, I was able to write this code to figure out author name.
{
class Program
{
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create("C:\\Users\\ile_xml.xml");
while(reader.Read())
{
if((reader.NodeType == XmlNodeType.Element) && (reader.Name == "author"))
{
reader.Read();
reader.Read();
if((reader.NodeType == XmlNodeType.Element) && (reader.Name == "style") && reader.HasAttributes)
{
var val = reader.ReadInnerXml();
Console.WriteLine("Display:" + reader.GetAttribute("author"));
}
}
}
}
}
}
The above code seems to be very inefficient and I am looking for ways to improve this or do it in a better way.

This will give you the correct result:-
XDocument xdoc = XDocument.Load(#"YourXMLfilePath");
var result = xdoc.Root.Elements("record")
.Select(x => new
{
Name = (string)x.Element("database").Attribute("name"),
Number = (string)x.Element("rec-number")
});

//Helpfull namespaces:
using System.Xml.Linq;
using System.Xml.XPath;
using System.Xml.Serialization;
static void Main(string[] args)
{
//Your snippet, which didn't work on my machine:
XmlReader reader = XmlReader.Create("C:\\Users\\Public\\ile_xml.xml");
while (reader.Read())
{
if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "author"))
{
reader.Read();
reader.Read();
if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "style") && reader.HasAttributes)
{
var val = reader.ReadInnerXml();
Console.WriteLine("Display:" + reader.GetAttribute("author"));
}
}
}
//Should produce the results you are looking for:
XmlNodeList xmlNodeList;
XmlDocument xDoc = new XmlDocument();
XmlReaderSettings xrs = new XmlReaderSettings();
xrs.DtdProcessing = DtdProcessing.Parse;
//Get Authors from XML Source
using (XmlReader reader2 = XmlReader.Create("C:\\Users\\Public\\ile_xml.xml"))
{
xDoc.Load(reader2);
xmlNodeList = xDoc.SelectNodes("records/record/contributors/authors/author");
}
foreach (XmlNode node in xmlNodeList)
{
Console.WriteLine(node.InnerText);//.InnerXML to include style tags.
};
}
xpath will help find the information you need. Hopefully the above will get you closer with xdoc.
Another pattern I have recently adopted is to serialize the xml into c# class (or in this case a List) and then use LINQ to manipulate as desired.
this was helpful to me: Deserializing XML to Objects in C#

Related

Read particular depth XML ELEMENTS using XmlReader in C#

I have an XML file with some nodes at different depths. I need a code to read only specific depth nodes but not all, and only by using XmlReader in C#.
Can someone help me in this?
Below is my XML structure. I want to read only "Depth2" nodes.
<Depth0>
<Depth1>
<Depth2/>
<Depth2/>
<Depth2/>
</Depth1>
<Depth1>
<Depth2/>
<Depth2/>
<Depth2/>
</Depth1>
<Depth1>
<Depth2/>
<Depth2/>
<Depth2/>
</Depth1>
</Depth0>
Code:
using (var reader = XmlReader.Create("D:\\xyz.xml"))
{
while (reader.Read())
{
if (reader.Depth == 4 && reader.NodeType == XmlNodeType.Element)
{
XmlReader chnode = reader.ReadSubtree();
AddItems(chnode);
}
else
reader.MoveToElement();
}
}
You can use the Descendants function:
var result = XDocument.Load("data.xml").Root
.Descendants("Depth2");
It will find for you all the Depth2 elements anywhere under the xml's Root
For an XmlReader way you can:
List<XmlNode> nodes = new List<XmlNode>();
using (var reader = XmlReader.Create("data.xml"))
{
XmlDocument document = new XmlDocument();
while (reader.Read())
{
if (reader.Depth == 2 && reader.NodeType == XmlNodeType.Element)
{
XmlNode node = document.CreateNode(XmlNodeType.Element, reader.Name, "");
//Here I just added all the inner xml but you can do whatever you need
node.InnerXml = reader.ReadInnerXml();
nodes.Add(node);
}
reader.MoveToElement();
}
}

Read the data from XML file into flat files(txt) and with formated data

I have an XML file with nodes and data...I need to write that into a text file as normal data. The nodes being the headers of the data
that follow.
EG XML:
<Bank>
<accountholder>Georgina Wax</accountholder>
<accountnumber>408999703657</accountnumber>
<accounttype>cheque</accounttype>
<bankname>National Bank</bankname>
<branch>Africa</branch>
<amount>2750.00</amount>
<date>12/01/2012</date>
</Bank>
To txt file and formatted as :
accountholder accountnumber accounttype bankname
Georgina Wax 408999703657 cheque National Bank
I can't seem to have it to have spaces between the data and hearders.
Below is what I tried :
StreamWriter writer = File.CreateText(#"C:\\Test.txt");
XmlDocument doc = new XmlDocument();
doc.Load(#"C:\\bank.xml");
writer.WriteLine(string.Join("|",doc.SelectSingleNode("/debitorders/deduction").ChildNodes.C ast<XmlElement>().Select(e => doc.SelectSingleNode("/debitorders/deduction/bankname").ToString())));
foreach (XmlElement book in doc.SelectNodes("/debitorders/deduction"))
{
writer.WriteLine(book.ChildNodes.Cast<XmlElement>().Select(e => e.InnerText).ToArray());
}
Please help.
This will produce output like you want.
private static void LoadAndWriteXML()
{
string headerFiles = "";
string values = "";
using (XmlReader reader = XmlReader.Create(#"C:\\bank.xml"))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && !reader.Name.Equals("Bank")) // we have to skip root node means bank node.
{
headerFiles += reader.Name + " ";
values += reader.ReadString() + " ";
}
}
}
StreamWriter writer = new StreamWriter(#"C:\\Test.txt");
writer.WriteLine(headerFiles.Trim());
writer.WriteLine(values.Trim());
writer.Close();
}
XDocument xdoc = new XDocument();
xdoc = XDocument.Load(fname);
xdoc.Save(fname1);
will save the file with the tags alignment formating

Read Large XML file using XPath

I am trying to read windows update package.xml file which is roughly 65mb in size, I am trying to just grab the URL attribute using Xpath but for some odd reason my object always returns empty. Here is my code:
doc.Load(#".\package.xml");
string xpath= "/OfflineSyncPackage/FileLocations/FileLocation/#Url";
XmlNodeList nodeList2 = doc.SelectNodes(xpath);
I have also tried using XmlReader which is also not working for me:
string packXML = #".\package.xml";
using (XmlReader xr = XmlReader.Create(packXML))
{
while (xr.Read())
{
switch (xr.NodeType)
{
case XmlNodeType.Element:
if (xr.Name == "OfflineSyncPackage")
{
xr.ReadStartElement("FileLocations");
if (xr.Name == "FileLocations")
{
if (xr.Name == "FileLocation")
{
}
}
}
break;
}
}
}
The package.xml file can be found in package.cab which is in this file: http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab
What is the best way to do this as I do not want to load whole file into memory due to size
Any advice is appreciated! Thank you
I figured this out finally!
public void ParseXML(string XMLPath)
{
XmlReader xmlReader = XmlReader.Create(XMLPath);
while (xmlReader.Read())
{
if (xmlReader.Name.Equals("FileLocation") && (xmlReader.NodeType == XmlNodeType.Element))
{
string url = xmlReader.GetAttribute("Url");
}
}
}

Failing To Parse the XML File in C#

I am a c++ developer and I started working on a C# WPF project. I have a method which should read the xml file. In my c++ application I could do it very efficiently but in WPF I am not sure how to approach the problem. Let me show you my code:
// When Browse Button is clicked this method is called
private void ExecuteScriptFileDialog()
{
var dialog = new OpenFileDialog { InitialDirectory = _defaultPath };
dialog.DefaultExt = ".xml";
dialog.Filter = "XML Files (*.xml)|*.xml";
dialog.ShowDialog();
ScriptPath = dialog.FileName; //ScriptPath contains the Path of the Xml File
if (File.Exists(ScriptPath))
{
LoadAardvarkScript(ScriptPath);
}
}
public void LoadAardvarkScript(string ScriptPath)
{
// I should read the xml file
}
I had achieved in C++ as follows:
File file = m_selectScript->getCurrentFile(); //m_selectScript is combobox name
if(file.exists())
{
LoadAardvarkScript(file);
}
void LoadAardvarkScript(File file)
{
XmlDocument xmlDoc(file);
//Get the main xml element
XmlElement *mainElement = xmlDoc.getDocumentElement();
XmlElement *childElement = NULL;
XmlElement *e = NULL;
int index = 0;
if(!mainElement )
{
//Not a valid XML file.
return ;
}
//Reading configurations...
if(mainElement->hasTagName("aardvark"))
{
forEachXmlChildElement (*mainElement, childElement)
{
//Read Board Name
if (childElement->hasTagName ("i2c_write"))
{
// Some code
}
}
}
How can I get the tagname of both mainElement and childelem as done in my c++ code? :)
not knowing the layout of your xml file here is an example that you can use if you know how to use Linq
class Program
{
static void Main(string[] args)
{
XElement main = XElement.Load(#"users.xml");
var results = main.Descendants("User")
.Descendants("Name")
.Where(e => e.Value == "John Doe")
.Select(e => e.Parent)
.Descendants("test")
.Select(e => new { date = e.Descendants("Date").FirstOrDefault().Value, points = e.Descendants("points").FirstOrDefault().Value });
foreach (var result in results)
Console.WriteLine("{0}, {1}", result.date, result.points);
Console.ReadLine();
}
}
you could also use XPATH as well to parse xml file.. but would really need to see the xml file layout
if you want to do it using xmlreader
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
namespace XmlReading
{
class Program
{
static void Main(string[] args)
{
//Create an instance of the XmlTextReader and call Read method to read the file
XmlTextReader textReader = new XmlTextReader("C:\\myxml.xml");
textReader.Read();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(textReader);
XmlNodeList BCode = xmlDoc.GetElementsByTagName("Brandcode");
XmlNodeList BName = xmlDoc.GetElementsByTagName("Brandname");
for (int i = 0; i < BCode.Count; i++)
{
if (BCode[i].InnerText == "001")
Console.WriteLine(BName[i].InnerText);
}
Console.ReadLine();
}
}
}
Use LINQ queries to extract data from xml (XDocument)
Refer this link for further insights on XLinq.
Sample XML :
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<People>
<Person id="1">
<Name>Joe</Name>
<Age>35</Age>
<Job>Manager</Job>
</Person>
<Person id="2">
<Name>Jason</Name>
<Age>18</Age>
<Job>Software Engineer</Job>
</Person>
</People>
Sample Linq query:
var names = (from person in Xdocument.Load("People.xml").Descendants("Person")
where int.Parse(person.Element("Age").Value) < 30
select person.Element("Name").Value).ToList();
Names will be list of string. This query will return Jason as he is 18 yrs old.
Using Linq2Xml,
var xDoc = XDocument.Load("myfile.xml");
var result = xDoc.Descendants("i2c_write")
.Select(x => new
{
Addr = x.Attribute("addr").Value,
Count = x.Attribute("count").Value,
Radix = x.Attribute("radix").Value,
Value = x.Value,
Sleep = ((XElement)x.NextNode).Attribute("ms").Value
})
.ToList();
var khz = xDoc.Root.Element("i2c_bitrate").Attribute("khz").Value;

View all text of an element with XmlReader C#

I'm using an XmlReader to iterate through some XML. Some of the XML is actually HTML and I want to get the text content from the node.
Example XML:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<p>Here is some <b>data</b></p>
</data>
Example code:
using (XmlReader reader = new XmlReader(myUrl))
{
while (reader.Read())
{
if (reader.Name == "p")
{
// I want to get all the TEXT contents from the this node
myVar = reader.Value;
}
}
}
This doesn't get me all the contents. How do I get all the contents from the node in that situation?
Use ReadInnerXml:
StringReader myUrl = new StringReader(#"<?xml version=""1.0"" encoding=""UTF-8""?>
<data>
<p>Here is some <b>data</b></p>
</data>");
using (XmlReader reader = XmlReader.Create(myUrl))
{
while (reader.Read())
{
if (reader.Name == "p")
{
// I want to get all the TEXT contents from the this node
Console.WriteLine(reader.ReadInnerXml());
}
}
}
Or if you want to skip the <b> as well, you can use an aux reader for the subtree, and only read the text nodes:
StringReader myUrl = new StringReader(#"<?xml version=""1.0"" encoding=""UTF-8""?>
<data>
<p>Here is some <b>data</b></p>
</data>");
StringBuilder myVar = new StringBuilder();
using (XmlReader reader = XmlReader.Create(myUrl))
{
while (reader.Read())
{
if (reader.Name == "p")
{
XmlReader pReader = reader.ReadSubtree();
while (pReader.Read())
{
if (pReader.NodeType == XmlNodeType.Text)
{
myVar.Append(pReader.Value);
}
}
}
}
}
Console.WriteLine(myVar.ToString());
I can't upvote or comment on others' responses, so let me just say carlosfigueira hit the nail on the head, that's exactly how you read the text value of an element. his answer helped me immensely.
for the sake of exmeplification here's my code:
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
{
if (reader.Name == "CharCode")
{
switch (reader.ReadInnerXml())
{
case "EUR":
{
reader.ReadToNextSibling("Value");
label4.Text = reader.ReadInnerXml();
}
break;
case "USD":
{
reader.ReadToNextSibling("Value");
label3.Text = reader.ReadInnerXml();
}
break;
case "RUB":
{
reader.ReadToNextSibling("Value");
label5.Text = reader.ReadInnerXml();
}
break;
case "RON":
{
reader.ReadToNextSibling("Value");
label6.Text = reader.ReadInnerXml();
}
break;
}
}
}
break;
}
}
the file I'm reading can be found here: http://www.bnm.md/md/official_exchange_rates?get_xml=1&date=
(you have to add a date in DD.MM.YYYY format to it to get the .XML)
I suggest you use HtmlAgilityPack which is a mature and, stable library for doing this sort of thing. It takes care of fetching the html, converting it to xml, and allows you to select the nodes you'd like with XPATH.
In your case it would be as simple as executing
HtmlDocument doc = new HtmlWeb().Load(myUrl);
string text = doc.DocumentNode.SelectSingleNode("/data/p").InnerText;

Categories