Extract XML attribute values as variables in C# [duplicate] - c#

I am trying to get Absoluteentry tag's value from the below xml string, but its displaying objectrefrence not set exception
<?xml version="1.0" ?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Body>
<AddResponse xmlns="http://www.sap.com/SBO/DIS">
<PickListParams>
<Absoluteentry>120072</Absoluteentry>
</PickListParams>
</AddResponse>
</env:Body>
</env:Envelope>
Code
XDocument doc = XDocument.Parse(xmlstring);
doc.Element("Envelope").Element("Body").Element("AddResponse").Element("PickListParams").Element("Absoluteentry").Value;

Look at the XML:
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
...
That's the Envelope element in the namespace with URI "http://www.w3.org/2003/05/soap-envelope".
Now look at your code:
doc.Element("Envelope")...
That's looking for an Envelope element that's not in any namespace. You should specify the namespace - and the namespaces of the other elements you're looking for:
XNamespace env = "http://www.w3.org/2003/05/soap-envelope";
XNamespace responseNs = "http://www.sap.com/SBO/DIS";
XDocument doc = XDocument.Parse(xmlstring);
var result = doc.Element(env + "Envelope")
.Element(env + "Body")
.Element(responseNs + "AddResponse")
.Element(responseNs + "PickListParams")
.Element(responseNs + "Absoluteentry").Value;

You can use Descendants too. Descendants finds children at any level.
var result = doc.Element(env + "Envelope")
.Element(env + "Body")
.Descendants(responseNs + "Absoluteentry").Value;

Related

XDocument get and set values in XML nodes

I have this XML
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<wss:Security xmlns:wss="http://schemas.xmlsoap.org/ws/2002/12/secext">
<wss:UsernameToken>
<wss:Username>username</wss:Username>
<wss:Password>password</wss:Password>
<wss:Nonce></wss:Nonce>
<wss:Created></wss:Created>
</wss:UsernameToken>
</wss:Security>
</S:Header>
<S:Body>
<TaxRegistrationNumber>213213123</TaxRegistrationNumber>
<CompanyName>sadsadasd</CompanyName>
</S:Body>
</S:Envelope>
I want to accesses to the value of <wss:Username> and set a value in <wss:Nonce> node.
I already try 3 ways to get value of <wss:Username> on C# project:
First:
XDocument xmlFile = XDocument.Load(xmlpathfile);
XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable());
ns.AddNamespace("wss", "http://schemas.xmlsoap.org/ws/2002/12/secext/");
XElement UserFinanc = xmlFile.XPathSelectElement("wss:Security/wss:UsernameToken/wss:Username", ns);
Second:
XDocument xmlFile = XDocument.Load(xmlpathfile);
XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable());
var element = xmlFile.Descendants(wss + "Security").Descendants(wss + "UsernameToken").Where(x => x.Descendants(wss + "Username").Any(y => y.Value != "")).First().Element(wss + "UsernameToken");
if (element != null)
MessageBox.Show(element.Element(wss + "Username").Value).Value);
Third:
string grandChild = (string) (from el in xmlFile.Descendants(wss + "Username") select el).First();
MsgBox.Show(grandChild);
I always have similar errors like 'The sequence contains no elements'
Your first attempt is almost right. There are a couple things missing:
The namespace defined in code must be an exact match to the one in the xml. In your case the namespace in code has an extra trailing slash. It should be http://schemas.xmlsoap.org/ws/2002/12/secext.
The XPath expression should be //wss:Security/wss:UsernameToken/wss:Username. The leading slashes basically mean "look for this node anywhere". Alternatively, you could write out the whole path begining with <S:Envelope>. You would need to add the soap envelope namespace to your code as well.

Attribute value in root node of xml Linq to XML

While parsing xml by linq to xml, I came across a strange behaviour (atleast to me). Below is the first xml I parsed
`<?xml version="1.0" encoding="UTF-8"?>
<TestRun>
<UnitTestResult testName = "Arun" outcome = "i">
</UnitTestResult>
<UnitTestResult testName = "Arun1" outcome = "i">
</UnitTestResult>
</TestRun>`
My Code looks like
XDocument doc = XDocument.Parse(fileContents);
var result = doc.Descendants("UnitTestResult");
The above works fine. But If my root node contain attributes the same code is not working. What could be the reason. XML sample below
<?xml version="1.0" encoding="UTF-8"?>
<TestRun id="7903b4ff-8706-4379-b9e8-567034b70abb" name="inaambika#INBELW013312A 2016-02-26 16:55:14" runUser="STC\inaambika" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<UnitTestResult testName = "Arun" outcome = "i">
</UnitTestResult>
<UnitTestResult testName = "Arun1" outcome = "i">
</UnitTestResult>
</TestRun>
XDocument doc = XDocument.Parse(fileContents);
var result = doc.Descendants("UnitTestResult");
This one below is not ordinary attribute, it is default namespace declaration :
xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010"
Having default namespace declared at the root element level, the root element and all descendant elements without prefix (in this case it means all elements in the posted XML) are in that namespace.
You can use XNamespace + element-local-name to reference element in namespace :
XDocument doc = XDocument.Parse(fileContents);
XNamespace d = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010"
var result = doc.Descendants(d+"UnitTestResult");

How do I get an attribute with namespace?

<?xml version="1.0" encoding="UTF-8"?>
<Root xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Element xsi:Attribute="Test"></Element>
</Root>
I'm trying to read the "xsi:Attribute" attribute; the code is like this:
var doc = XDocument.Load(new StringReader(xmlText));
var node = doc.Root.Descendants().First();
XNamespace myNamespace = "xsi";
var attribute = node.Attributes(myNamespace + "Attribute").First();
It throws a "Sequence contains no elements" exception in the last line. What am I doing wrong?
You need to use the actual namespace, not "xsi", which is just a local lookup within the XML file itself for the real namespace:
XNamespace myNamespace = "http://www.w3.org/2001/XMLSchema-instance";
Try this (I believe its more generic):
XNamespace myNamespace = doc.Root.GetNamespaceOfPrefix("xsi");

How to read namespaces c# LINQ to XML

How to read namespaces from code? I want to get xmlns:es, xmlns:un, xmlns:xn attribute values, but I get null attributes, How do I read it?
I try like this:
XNamespace xmlns = xdoc.Root.Attribute("xmlns").Value;
XNamespace ES = xdoc.Root.Attribute(xmlns + "es").Value;
XML:
<?xml version="1.0" encoding="UTF-8"?>
<xmlFile xmlns:es="my.xsd"
xmlns="not_my.xsd">
...
</xmlFile>
XNamespace defns = xdoc.Root.GetDefaultNamespace();
XNamespace es = xdoc.Root.GetNamespaceOfPrefix("es");

XDocument and Linq returns null if the element has xmlns attribute

Newbie with XDocuments and Linq, please suggest a solution to retrieve the data from a particular tag in the xml string:
If I have a XML string from webservice response (I formatted xml for ease):
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetCashFlowReportResponse xmlns="http://tempuri.org/">
<GetCashFlowReportPdf>Hello!</GetCashFlowReportPdf>
</GetCashFlowReportResponse>
</soap:Body>
</soap:Envelope>
Using the following code, I can get the value only if GetCashFlowReportResponse tag doesn't have "xmlns" attribute. Not sure why? Otherwise, it always return null.
string inputString = "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><soap:Body><GetCashFlowReportResponse xmlns=\"http://tempuri.org/\"><GetCashFlowReportPdf>Hello!</GetCashFlowReportPdf></GetCashFlowReportResponse></soap:Body></soap:Envelope>"
XDocument xDoc = XDocument.Parse(inputString);
//XNamespace ns = "http://tempuri.org/";
XNamespace ns = XNamespace.Get("http://tempuri.org/");
var data = from c in xDoc.Descendants(ns + "GetCashFlowReportResponse")
select (string)c.Element("GetCashFlowReportPdf");
foreach (string val in data)
{
Console.WriteLine(val);
}
I can't change the web service to remove that attribute. IS there a better way to read the response and get the actual data back to the user (in more readable form)?
Edited:
SOLUTION:
XDocument xDoc = XDocument.Parse(inputString);
XNamespace ns = "http://tempuri.org/";
var data = from c in xDoc.Descendants(ns + "GetCashFlowReportResponse")
select (string)c.Element(ns + "GetCashFlowReportPdf");
foreach (string val in data)
{
Console.WriteLine(val);
}
Note: Even if all the child elements doesn't have the namespace attribute, the code will work if you add the "ns" to the element as I guess childs inherit the namespace from parent (see response from SLaks).
You need to include the namespace:
XNamespace ns = "http://tempuri.org/";
xDoc.Descendants(ns + "GetCashFlowReportResponse")
XName qualifiedName = XName.Get("GetCashFlowReportResponse",
"http://tempuri.org/");
var data = from d in xDoc.Descendants(qualifiedName)
Just ask for the elements using the qualified names:
// create a XML namespace object
XNamespace ns = XNamespace.Get("http://tempuri.org/");
var data = from c in xDoc.Descendants(ns + "GetCashFlowReportResponse")
select (string)c.Element(ns + "GetCashFlowReportPdf");
Note the use of the overloaded + operator that creates a QName with a XML namespace and a local name string.

Categories

Resources