Use XMLReader to access child nodes with duplicate names - c#

Using the XML fragment below, how would I access the child node <amt> of the node <salesTaxAmt> using XMLReader? If I iterate over the nodes looking for a node name of "amt" I return the last node amount of <sourceCurrAmt> which is 0.00.
<transactionUnit>
<transactionDetails>
<transactionId>11883382</transactionId>
<currencyAmount>
<amt>30.00</amt>
<currCode>USD</currCode>
</currencyAmount>
<gstAmount>
<amt>60.00</amt>
<currCode>USD</currCode>
</gstAmount>
<pstNqstAmt>
<amt>0.00</amt>
<currCode>USD</currCode>
</pstNqstAmt>
<salesTaxAmt>
<amt>1.00</amt>
<currCode>USD</currCode>
</salesTaxAmt>
<sourceCurrAmt>
<amt>0.00</amt>
</sourceCurrAmt>
</transactionDetails>
</transactionUnit>
Is the code below even the best way to do this?
Test Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
namespace TestConsole
{
public class ParseXML
{
static void Main(string[] args)
{
try
{
FileStream file;
XmlReader baseReader;
XmlTextReader reader;
XmlReaderSettings readerSettings;
file = new FileStream(#"C:\Data.xml", FileMode.Open, FileAccess.Read);
file.Seek(0, SeekOrigin.Begin);
reader = new XmlTextReader(file, XmlNodeType.Element, null);
reader.Normalization = false;
readerSettings = new XmlReaderSettings();
readerSettings.ConformanceLevel = ConformanceLevel.Fragment;
readerSettings.IgnoreWhitespace = false;
readerSettings.IgnoreComments = true;
readerSettings.CheckCharacters = false;
baseReader = XmlReader.Create(reader, readerSettings);
int x = 0;
while (baseReader.Read())
{
if (baseReader.Name.Equals("transactionUnit") && (baseReader.NodeType == XmlNodeType.Element))
{
string amt = null;
XmlReader inner = reader.ReadSubtree();
while (inner.Read())
{
if (inner.Name.Equals("ns:amt"))
{
amt = inner.ReadElementString();
}
}
Console.WriteLine("amt: {0}", amt);
inner.Close();
x = x + 1;
}
}
Console.WriteLine("{0} transactions found", x.ToString());
baseReader.Close();
file.Close();
}
catch (XmlException xe)
{
Console.WriteLine("XML Parsing Error: " + xe);
}
catch (IOException ioe)
{
Console.WriteLine("File I/O Error: " + ioe);
}
}
}
}

From your example code it looks like you can use LINQ to XML, this would be a more succinct solution for your problem:
XDocument xDoc = XDocument.Load(#"C:\Data.xml");
string amt = xDoc.Descendants("salesTaxAmt")
.Elements("amt")
.Single().Value;
If you can use it LINQ to XML should definitely be your choice, it provides an easy and powerful syntax for retrieving data from XML and plays nicely with LINQ to objects.

A simpler way to get the node value is to use XPath.
Note that this is possible only if you node the path to the node whose value is to be retreived.
private string GetNodeValue(string xmlFilePath, string xpath)
{
string nodeValue = string.Empty;
using (StreamReader reader = new StreamReader(xmlFilePath))
{
XPathDocument xPathDocument = new XPathDocument(reader);
XPathNavigator navigator = xPathDocument.CreateNavigator();
XPathNodeIterator iter = navigator.Select(xpath);
iter.MoveNext();
nodeValue = iter.Current.Value;
//iter.Current.ValueAsDouble;
}
return nodeValue;
}
The usage for your sample xml should be:
string nodeValue = GetNodeValue(#"C:\Data.xml", "//transactionUnit/transactionDetails/salesTaxAmt/amt");
In addition, you could rename the method as 'GetNodeValueAsDouble' and use iter.Current.ValueAsDouble instead of iter.Current.Value to get the double value.
More Information
XPathNavigator class
XPathDocument class

Related

How to retrieve all Elements from XML file using c#

I am trying to retrieve all elements from an XML file, but I just can reach one, is there any way I can retrieve all?
HttpWebResponse objResponse = (HttpWebResponse)objRequest.GetResponse();
using (XmlReader reader = XmlReader.Create(new StreamReader(objResponse.GetResponseStream())))
{
while (reader.Read())
{
#region Get Credit Score
//if (reader.ReadToDescendant("results"))
if (reader.ReadToDescendant("ssnMatchIndicator"))
{
string ssnMatchIndicator = reader.Value;
}
if (reader.ReadToDescendant("fileHitIndicator"))
{
reader.Read();//this moves reader to next node which is text
result = reader.Value; //this might give value than
Res.Response = true;
Res.SocialSecurityScore = result.ToString();
//break;
}
else
{
Res.Response = false;
Res.SocialSecurityScore = "Your credit score might not be available. Please contact support";
}
#endregion
#region Get fileHitIndicator
if (reader.ReadToDescendant("fileHitIndicator"))
{
reader.Read();
Res.fileHitIndicator = reader.Value;
//break;
}
#endregion
}
}
Can somebody help me out with this issue?
I am also using objResponse.GetResponseStream() because the XML comes from a response from server.
Thanks a lot in advance.
Try this :
XmlDataDocument xmldoc = new XmlDataDocument();
XmlNodeList xmlnode ;
int i = 0;
string str = null;
FileStream fs = new FileStream("product.xml", FileMode.Open, FileAccess.Read);
xmldoc.Load(fs);
xmlnode = xmldoc.GetElementsByTagName("Product");
for (i = 0; i <= xmlnode.Count - 1; i++)
{
xmlnode[i].ChildNodes.Item(0).InnerText.Trim();
str = xmlnode[i].ChildNodes.Item(0).InnerText.Trim() + " " + xmlnode[i].ChildNodes.Item(1).InnerText.Trim() + " " + xmlnode[i].ChildNodes.Item(2).InnerText.Trim();
MessageBox.Show (str);
}
I don't know why what you're doing is not working, but I wouldn't use that method. I've found the following to work well. Whether you're getting the xml from a stream, just put it into a string and bang...
StreamReader reader = new StreamReader(sourcepath);
string xml = reader.ReadToEnd();
reader.Close();
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlNodeList list = doc.GetElementsByTagName("*");
foreach (XmlNode nd in list)
{
switch (nd.Name)
{
case "ContactID":
var ContactIdent = nd.InnerText;
break;
case "ContactName":
var ContactName = nd.InnerText;
break;
}
}
To capture what is between the Xml tags, if there are no child Xml tags, use the InnerText property, e.g. XmlNode.InnerText. To capture what is between the quotes in the nodes' attributes, use XmlAttribute.Value.
As for iterating through the attributes, if one of your nodes has attributes, such as the elements "Name", "SpectralType" and "Orbit" in the Xml here:
<System>
<Star Name="Epsilon Eridani" SpectralType="K2v">
<Planets>
<Planet Orbit="1">Bill</Planet>
<Planet Orbit="2">Moira</Planet>
</Planets>
</Star>
</System>
Detect them using the Attributes property, and iterate through them as shown:
if (nd.Attributes.Count > 0)
{
XmlAttributeCollection coll = nd.Attributes;
foreach (XmlAttribute cn in coll)
{
switch (cn.Name)
{
case "Name":
thisStar.Name = cn.Value;
break;
case "SpectralType":
thisStar.SpectralClass = cn.Value;
break;
}
}
}
You might find some more useful information HERE.

Reading particular element of XML if value increase the threshold

I can read an XML file named 101.xml in which I can read element named light having two values say 1280 and 128, I can display it. But I want to display only that value which is greater than 800. Check image for XML file.
This is what I did to read both light elements in Listbox:
READING NEW FILE:
XmlTextReader Reader = new XmlTextReader(#"101.xml");
XmlDocument doc = new XmlDocument();
doc.Load(Reader);
XPathNavigator nav = doc.CreateNavigator();
//compile xpath
XPathExpression expr;
expr = nav.Compile("/MotePacket/ParsedDataElement[Name='light']");
XPathNodeIterator iterator = nav.Select(expr);
//iterate node set and see values in list box
listBox1.Items.Clear();
try
{
while (iterator.MoveNext())
{
XPathNavigator nav2 = iterator.Current.Clone();
listBox1.Items.Add("content and value: " + nav2);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
This is very bad xml format. Recommend changing. Here is code to parse xml
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> parsedDataElements = doc.Descendants("ParsedDataElement").ToList();
for (int i = 0; i < parsedDataElements.Count; i += 2)
{
DataElement newDataElement = new DataElement();
DataElement.elements.Add(newDataElement);
newDataElement.temperature = (double)parsedDataElements[i].Element("ConvertedValue");
newDataElement.light = (int)parsedDataElements[i + 1].Element("ConvertedValue");
}
}
}
public class DataElement
{
public static List<DataElement> elements = new List<DataElement>();
public double temperature { get; set; }
public int light { get; set; }
}
}
As i mentioned in the comment to the question, you can use XDocument class together with Linq query to filter xml data.
See:
XDocument xdoc = XDocument.Load(fullfilename);
var data = xdoc.Descendants("ParsedDataElement")
.Where(x=>x.Element("Name").Value == "light" && Double.Parse(x.Element("ConvertedValue").Value)>800)
.Select(x=> new
{
Name = x.Element("Name").Value,
ConvertedValue = Double.Parse(x.Element("ConvertedValue").Value)
})
.ToList();
Now, you are able to insert data into ListBox object by using foreach loop or by setting DataSource.
For further details, please see:
Basic Queries (LINQ to XML) (C#)
How to: Bind a Windows Forms ComboBox or ListBox Control to Data
Good luck!

How to read xml node title and it's value?

I have following xml and want to parse it and read every node value and node title.
I can read the value but can't read the node title. I used reader.Name but it always returns empty string. How can I Read node title too?
string ExceptionDetails="<names><FirstName>John</FirstName><SecondName>White</SecondName></names>";
using (XmlReader reader = XmlReader.Create(new StringReader(ExceptionDetails)))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
while (reader.Read())
{
if (reader.HasValue)
{
sb.Append()+":"; //Read tag title
sb.Append(reader.Value); sb.Append("<br/>");
}
}
}
}
}
result:
FirstName:John
SecondName:white
You can use XmlReader.Name to get the name of the current node. In case of an element, this is the tag name. In order to remember the last tag name before the text, you need to store it in a variable before you add it to the StringBuilder:
using System;
using System.Xml;
using System.IO;
using System.Text;
public class Test
{
public static void Main()
{
StringBuilder sb = new StringBuilder();
string ExceptionDetails="<names><FirstName>John</FirstName><SecondName>White</SecondName></names>";
using (XmlReader reader = XmlReader.Create(new StringReader(ExceptionDetails)))
{
string tagName = string.Empty;
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
tagName = reader.Name;
else if (reader.NodeType == XmlNodeType.Text)
{
sb.Append(tagName);
sb.Append(":"); //Read tag title
sb.Append(reader.Value); sb.Append("<br/>");
}
}
}
Console.WriteLine(sb.ToString());
}
}
You can run the sample here.
XDocument doc = XDocument.Load(Your XML);
var elements = doc.Descendants( "names" );
Dictionary<string, string> keyValues = new Dictionary<string, string>();
for (int i = 0; i < elements.Count; i++)
{
string key = elements[i].Attributes["key"].Value.ToString();
string value = elements[i].Attributes["value"].Value.ToString();
keyValues.Add(key,value);
}

How do I retrieve data that meets a specific condition in an XML file?

I have an XML file, and I want to retrieve the brandname when brandcode is 001 from the following XML.
<Root>
- <data>
<Companycode>TF</Companycode>
<Productcode>00001</Productcode>
<Productname>VPU</Productname>
<Brandcode>001</Brandcode>
<Brandname>DB</Brandname>
</data>
- <data>
<Companycode>TF</Companycode>
<Productcode>00002</Productcode>
<Productname>SENDERCARD</Productname>
<Brandcode>002</Brandcode>
<Brandname>LINSN</Brandname>
</data>
</Root>
This is my code; I need to assign Brand Name here:
XmlTextReader textReader = new XmlTextReader(#"codedata.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")
{
string brandname = BName[i].InnerText;
}
//Console.WriteLine(BName[i].InnerText);
}
Try In this way...
using System;
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("D:\\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();
}
}
}
Try this XPath: //Brandname[../Brandcode[text()='001']]
Using XmlDocument.SelectSingleNode:
var document = new XmlDocument();
var Brandcode = "001";
var xpath = String.Format(#"//Brandname[../Brandcode[text()='{0}']]",
Brandcode);
var Brandname = document.SelectSingleNode(xpath).InnerText;
and using XDocument.XPathSelectElement:
var document = XDocument.Load("fileName");
var name = document.XPathSelectElement(xpath).Value;
YOu can use Xml.Linq namespace, with XDocument class to load and select the exact element from the xml file:
XDocument doc = XDocument.Load(#"filePath");
var query = doc.Descendants("Root").Where(w => w.Element("Brandcode").Value == "001").Select(s => new { s.Element("Brandname").Value }).FirstOrDefault();
string brandname = query.Value;
Define the brandname as string and assign null to it and then use the brand name where ever you want....
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("D:\\myxml.xml");
textReader.Read();
string brandname = null;
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")
{
brandname = BName[i].InnerText;
}
}
Console.WriteLine(brandname);
Console.ReadLine();
}
}
}

How keep carriage return from parsing XML

I am looking on Internet how keep the carriage return from XML data but I could not find the answer, so I'm here :)
The objective is to write in a file the content of a XML data. So, if the value of the node contains some "\r\n" data, the soft need to write them in file in order to create new line, but it doesn't write, even with space:preserve.
Here is my test class:
XElement xRootNode = new XElement("DOCS");
XElement xData = null;
//XNamespace ns = XNamespace.Xml;
//XAttribute spacePreserve = new XAttribute(ns+"space", "preserve");
//xRootNode.Add(spacePreserve);
xData = new XElement("DOC");
xData.Add(new XAttribute("FILENAME", "c:\\titi\\prout.txt"));
xData.Add(new XAttribute("MODE", "APPEND"));
xData.Add("Hi my name is Baptiste\r\nI'm a lazy boy");
xRootNode.Add(xData);
bool result = Tools.writeToFile(xRootNode.ToString());
And here is my process class:
try
{
XElement xRootNode = XElement.Parse(xmlInputFiles);
String filename = xRootNode.Element(xNodeDoc).Attribute(xAttributeFilename).Value.ToString();
Boolean mode = false;
try
{
mode = xRootNode.Element(xNodeDoc).Attribute(xWriteMode).Value.ToString().ToUpper().Equals(xWriteModeAppend);
}
catch (Exception e)
{
mode = false;
}
String value = xRootNode.Element(xNodeDoc).Value;
StreamWriter destFile = new StreamWriter(filename, mode, System.Text.Encoding.Unicode);
destFile.Write(value);
destFile.Close();
return true;
}
catch (Exception e)
{
return false;
}
Does anybody have an idea?
If you want to preserve cr lf in element or attribute content when saving a XDocument or XElement you can do that by using certain XmlWriterSettings, namely NewLineHandling to Entitize:
string fileName = "XmlOuputTest1.xml";
string attValue = "Line1.\r\nLine2.";
string elementValue = "Line1.\r\nLine2.\r\nLine3.";
XmlWriterSettings xws = new XmlWriterSettings();
xws.NewLineHandling = NewLineHandling.Entitize;
XDocument doc = new XDocument(new XElement("root",
new XAttribute("test", attValue),
elementValue));
using (XmlWriter xw = XmlWriter.Create(fileName, xws))
{
doc.Save(xw);
}
doc = XDocument.Load(fileName);
Console.WriteLine("att value: {0}; element value: {1}.",
attValue == doc.Root.Attribute("test").Value,
elementValue == doc.Root.Value);
In that example the value are preserved in the round trip of saving and loading as the output of the sample is "att value: True; element value: True."
Heres a useful link I found for parsing an Xml string with carraige returns, line feeds in it.
howto-correctly-parse-using-xelementparse-for-strings-that-contain-newline-character-in
It may help those who are parsing an Xml string.
For those who can't be bothered to click it says use an XmlTextReader instead
XmlTextReader xtr = new XmlTextReader(new StringReader(xml));
XElement items = XElement.Load(xtr);
foreach (string desc in items.Elements("Item").Select(i => (string)i.Attribute("Description")))
{
Console.WriteLine("|{0}|", desc);
}

Categories