Reading attribute from an XML file - c#

I have an XML file which looks like this
<SendInvoiceResult xmlns="http://tempuri.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" IsSucceded="true">
<Value Id="123456" Number="1" InvoiceScenario="Scenario" />
</SendInvoiceResult>
I'm trying to read the attributes of this file but so far all my tries based on other questions on Stackoverflow either returned null or "Object reference not set to an instance of an object" error.
My latest try is this:
var testXml = new XmlDocument();
testXml.LoadXml(test);
var node = testXml.SelectSingleNode("/SendInvoiceResult/Value");
var id = node.Attributes["Id"].Value;
This approach returns "Object reference not set to an instance of an object" too. I'm wondering if something is wrong with the way XML is structures at this point.
I'm open to new methods and suggestions of course, all I need is to read the values of attributes in this and other similar XML files.

Use XDocument. It is much more modern and capable.
var doc = XElement.Load(test);
var id = doc.Root.Element("Value").Attribute("Id").Value;

You must define xml namespace.
var doc = new XmlDocument();
doc.Load("test.txt");
var manager = new XmlNamespaceManager(doc.NameTable);
manager.AddNamespace("ns", "http://tempuri.org/");
var node = doc.SelectSingleNode("/ns:SendInvoiceResult/ns:Value", manager);
var id = node.Attributes["Id"].Value;
Console.WriteLine(id);
Better use modern and more convenient linq to xml.
using System.Xml.Linq;
var doc = XDocument.Load("test.txt");
XNamespace ns = "http://tempuri.org/";
var id = doc.Root.Element(ns + "Value").Attribute("Id").Value;
Console.WriteLine(id);

Related

How do I read xml in C#? [duplicate]

How can I read an XML attribute using C#'s XmlDocument?
I have an XML file which looks somewhat like this:
<?xml version="1.0" encoding="utf-8" ?>
<MyConfiguration xmlns="http://tempuri.org/myOwnSchema.xsd" SuperNumber="1" SuperString="whipcream">
<Other stuff />
</MyConfiguration>
How would I read the XML attributes SuperNumber and SuperString?
Currently I'm using XmlDocument, and I get the values in between using XmlDocument's GetElementsByTagName() and that works really well. I just can't figure out how to get the attributes?
XmlNodeList elemList = doc.GetElementsByTagName(...);
for (int i = 0; i < elemList.Count; i++)
{
string attrVal = elemList[i].Attributes["SuperString"].Value;
}
You should look into XPath. Once you start using it, you'll find its a lot more efficient and easier to code than iterating through lists. It also lets you directly get the things you want.
Then the code would be something similar to
string attrVal = doc.SelectSingleNode("/MyConfiguration/#SuperNumber").Value;
Note that XPath 3.0 became a W3C Recommendation on April 8, 2014.
You can migrate to XDocument instead of XmlDocument and then use Linq if you prefer that syntax. Something like:
var q = (from myConfig in xDoc.Elements("MyConfiguration")
select myConfig.Attribute("SuperString").Value)
.First();
I have an Xml File books.xml
<ParameterDBConfig>
<ID Definition="1" />
</ParameterDBConfig>
Program:
XmlDocument doc = new XmlDocument();
doc.Load("D:/siva/books.xml");
XmlNodeList elemList = doc.GetElementsByTagName("ID");
for (int i = 0; i < elemList.Count; i++)
{
string attrVal = elemList[i].Attributes["Definition"].Value;
}
Now, attrVal has the value of ID.
XmlDocument.Attributes perhaps? (Which has a method GetNamedItem that will presumably do what you want, although I've always just iterated the attribute collection)
Assuming your example document is in the string variable doc
> XDocument.Parse(doc).Root.Attribute("SuperNumber")
1
If your XML contains namespaces, then you can do the following in order to obtain the value of an attribute:
var xmlDoc = new XmlDocument();
// content is your XML as string
xmlDoc.LoadXml(content);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
// make sure the namespace identifier, URN in this case, matches what you have in your XML
nsmgr.AddNamespace("ns", "urn:oasis:names:tc:SAML:2.0:protocol");
// get the value of Destination attribute from within the Response node with a prefix who's identifier is "urn:oasis:names:tc:SAML:2.0:protocol" using XPath
var str = xmlDoc.SelectSingleNode("/ns:Response/#Destination", nsmgr);
if (str != null)
{
Console.WriteLine(str.Value);
}
More on XML namespaces here and here.

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.

Find an element with a period in it's name

I have the following XML:
<T24.MESSAGE>
<TRANSACTION.TYPE>CHEQUE.ON.THEM.AUTH</TRANSACTION.TYPE>
<TRANSACTION.ID>FT14273PKQ14</TRANSACTION.ID>
</T24.MESSAGE>
I am trying to find the TRANSACTION.TYPE element, using the xpath query /TRANSACTION.TYPE. However, this is returning nothing, and I think it is because the element has a period in the name.
Is there a way to escape the period? Though according to the MS reference it isn't needed. http://msdn.microsoft.com/en-us/library/ms256199%28v=vs.110%29.aspx
Edit: I have also tried /T24.MESSAGE/TRANSACTION.TYPE and just TRANSACTION.TYPE and neither work.
Code I use to read it:
byte[] xmlBytes = Encoding.UTF8.GetBytes(strXML);
using (MemoryStream xmlStream = new MemoryStream(xmlBytes))
{
XPathDocument doc = new XPathDocument(XmlReader.Create(xmlStream, xmlReaderSettings));
var navigator = doc.CreateNavigator();
var node = navigator.SelectSingleNode("/TRANSACTION.TYPE"); //null
//...
}
Since you're using namespaces you might try using a namespace agnostic form in your code. It'd look like this
var node = navigator.SelectSingleNode("//*[local-name()='TRANSACTION.TYPE']");
The issue is with your namespace. You'll need to create a XmlNamespaceManager and pass that to your SelectSingleNode call.
In the below I've created a namespace of urn:test:
string strXML = #"<?xml version='1.0'?>
<T24.MESSAGE xmlns=""urn:Test"">
<TRANSACTION.TYPE>CHEQUE.ON.THEM.AUTH</TRANSACTION.TYPE>
<TRANSACTION.ID>FT14273PKQ14</TRANSACTION.ID>
</T24.MESSAGE>";
byte[] xmlBytes = Encoding.UTF8.GetBytes(strXML);
using (MemoryStream xmlStream = new MemoryStream(xmlBytes))
{
XPathDocument doc = new XPathDocument(XmlReader.Create(xmlStream));
var navigator = doc.CreateNavigator();
XmlNamespaceManager xmlnsManager = new XmlNamespaceManager(navigator.NameTable);
//Add the namespaces used. In this instance I'm setting a prefix of "t"
xmlnsManager.AddNamespace("t", "urn:Test");
//pass the XmlNamespaceManager to the call to SelectSingleNode
//the XPath also includes the root element
var node = navigator.SelectSingleNode("//t:T24.MESSAGE/t:TRANSACTION.TYPE", xmlnsManager);
Console.WriteLine(node.Name);
}
This code correctly ouptuts
TRANSACTION.TYPE
This certainly works
string yourXMLString = #"<T24.MESSAGE>
<TRANSACTION.TYPE>CHEQUE.ON.THEM.AUTH</TRANSACTION.TYPE>
<TRANSACTION.ID>FT14273PKQ14</TRANSACTION.ID>
</T24.MESSAGE>";
XDocument xDoc = XDocument.Parse(yourXMLString);
var res = xDoc.Descendants("T24.MESSAGE")
.Elements("TRANSACTION.TYPE");
result: <TRANSACTION.TYPE>CHEQUE.ON.THEM.AUTH</TRANSACTION.TYPE>

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();

Extract XML from SOAP envelope in c#

I have a response from web service in SOAP envelope as follows:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<ProcessTranResponse xmlns="http://www.polaris.co.uk/XRTEService/2009/03/">
<ProcessTranResult xmlns:a="http://schemas.datacontract.org/2004/07/XRTEService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:PrintFormFileNameContents i:nil="true"/>
<a:ResponseXML>response_message</a:ResponseXML>
</ProcessTranResult>
</ProcessTranResponse>
</s:Body>
And I want to get response_message to a string variable. I tried doing
XDocument doc = XDocument.Parse(Response);
XNamespace xmlnsa = "http://schemas.datacontract.org/2004/07/XRTEService";
var ResponseXML = doc.Descendants(xmlnsa + "ResponseXML");
And when I use watch I see in ResponseXML -> Results View[0] -> Value my response_message, but I can't figure out what is the next step to get to Value from C#.
XContainer.Descendants returns a collection of elements. You should then try something like this:
foreach (XElement el in ResponseXML)
{
Console.WriteLine(el.Value);
}
Or you can do something like this if you know that there is always only one response:
XDocument doc = XDocument.Parse(Response);
XNamespace xmlnsa = "http://schemas.datacontract.org/2004/07/XRTEService";
XElement ResponseXML = (from xml in XMLDoc.Descendants(xmlnsa + "ResponseXML")
select xml).FirstOrDefault();
string ResponseAsString = ResponseXML.Value;
You can employ several solutions to meet your purpose while you may wanna introduce the structure of xml content or not.
Static attitude
You can simply use this:
XmlDocument _doc = new XmlDocument();
doc.LoadXml(_stream.ReadToEnd());
Then find the desired data with something like this:
doc.LastChild.FirstChild.FirstChild.LastChild.InnerText;
Read xml structure
You can write some additional lines of code to introduce namespaces and other xml contents to find/map the available data, by taking a look at extracting-data-from-a-complex-xml-with-linq

Categories