Simplest way to get XML nodes with namespace? - c#

I have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<createTransactionResponse xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<messages>
<resultCode>Ok</resultCode>
<message>
<code>I00001</code>
<text>Successful.</text>
</message>
</messages>
<transactionResponse>
<responseCode>1</responseCode>
<authCode>25C10X</authCode>
<messages>
<message>
<code>1</code>
<description>This transaction has been approved.</description>
</message>
</messages>
</transactionResponse>
</createTransactionResponse>
What is the easiest way to get the value "Successful." from createTransactionResponse->messages->message->text?
Here is my code:
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("anet", "AnetApi/xml/v1/schema/AnetApiSchema.xsd");
var myNodes = doc.SelectNodes("//anet:messages", nsmgr);
myNodes returns 2 nodes. The innerxml of node[0] is:
<resultCode xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">Ok</resultCode><message xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">
<code>I00001</code>
<text>Successful.</text>
</message>
The innerxml of node[1] is:
<message xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">
<code>1</code>
<description>This transaction has been approved.</description>
</message>
My problem is I can't go any deeper than that.
//anet:messages/message yields nothing.
//anet:createTransactionResponse/messages yields nothing.
I'm just trying to get specific element values such as "I00001" and "25C10X".
What am I doing wrong?

Namespace bindings are inherited, so the child elements are in the same namespace as their parents here.
You need to add the missing namespace prefixes to your query:
//anet:messages/anet:message/anet:text
That said, I'd usually prefer LINQ to XML over XPath:
XNamespace ns = "AnetApi/xml/v1/schema/AnetApiSchema.xsd";
var root = XElement.Parse(xml);
var text = (string) root.Elements(ns + "messages")
.Descendants(ns + "text")
.Single();
See this fiddle for a working demo.

You can also use the LocalName property to avoid using the namespace:
XDocument root = XDocument.Parse(xml);
string text = root.Descendants()
.First(node => node.Name.LocalName == "messages").Elements()
.First(node => node.Name.LocalName == "message").Elements()
.First(node => node.Name.LocalName == "text").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.

Null return on XmlDocument.SelectSingleNode through valid xpath

I currently have the following code
nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
nsmgr.AddNamespace("soapenv", soapenv_namespace);
nsmgr.AddNamespace("de", de_namespace);
XmlNode xnEnvelope = xmlDoc.SelectSingleNode("//soapenv:Envelope", nsmgr);
XmlNode xnBody = xmlDoc.SelectSingleNode("//soapenv:Envelope/soapenv:Body", nsmgr);
XmlNode xnMessage = xnBody.SelectSingleNode("//soapenv:Envelope/soapenv:Body/message", nsmgr);
Which parses the following xml (truncated for readibility)
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<message xmlns="http://www.origostandards.com/schema/ce/v2.1/CEBondSingleContractDetailResponse" xmlns:ns2="http://www.origostandards.com/schema/ce/v2.1/CEBondSingleContractReferenceResponse" xmlns:ns3="http://www.origostandards.com/schema/ce/v2.1/CEBondSingleContractRequest" xmlns:ns4="http://www.origostandards.com/schema/tech/v1.0/SOAPFaultDetail">
<de:m_content .....
Problem is the line
XmlNode xnMessage = xnBody.SelectSingleNode("//soapenv:Envelope/soapenv:Body/message", nsmgr); returns null when i'd expect it to return the message element.
I highly suspect its to do with the blank namespace I have configured but I cant find the right combination of values to get it working.
Any pointers would be appreciated.
You have introduced default namespace here :
xmlns="http://www.origostandards.com/schema/ce/v2.1/CEBondSingleContractDetailResponse"
which causes message element and all of it's descendants without prefix to be recognized as in that default namespace (unless the descendant element has local default namespace). To access element in default namespace, simply register a prefix, for example d, and map it to the default namespace uri :
nsmgr.AddNamespace("d", "http://www.origostandards.com/schema/ce/v2.1/CEBondSingleContractDetailResponse");
Then use the newly registered prefix accordingly in the XPath expression :
XmlNode xnBody = xmlDoc.SelectSingleNode("//soapenv:Envelope/soapenv:Body", nsmgr);
XmlNode xnMessage = xnBody.SelectSingleNode("d:message", nsmgr);
This is working
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(#"C:\Users\DummyUser\Desktop\Noname1.xml");
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
nsmgr.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
nsmgr.AddNamespace("de", "http://www.origostandards.com/schema/ce/v2.1/CEBondSingleContractDetailResponse");
XmlNode xnEnvelope = xmlDoc.SelectSingleNode("//soapenv:Envelope", nsmgr);
XmlNode xnBody = xnEnvelope.SelectSingleNode("//soapenv:Body", nsmgr);
XmlNode xnMessage = xnBody.SelectSingleNode("de:message", nsmgr);
and xml file is
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<message xmlns="http://www.origostandards.com/schema/ce/v2.1/CEBondSingleContractDetailResponse" xmlns:ns2="http://www.origostandards.com/schema/ce/v2.1/CEBondSingleContractReferenceResponse"
xmlns:ns3="http://www.origostandards.com/schema/ce/v2.1/CEBondSingleContractRequest"
xmlns:ns4="http://www.origostandards.com/schema/tech/v1.0/SOAPFaultDetail">
This is my sample message
</message>
</soapenv:Body>
</soapenv:Envelope>

Select last XML node

I have this XML code:
<AriaGostarInformation>
<MenuInformation>
<MenuNames Name="0" href="default.aspx">home</MenuNames>
<SubMenuNames parentName="1">
fgfgfgfgs
</SubMenuNames>
<SubMenuNames parentName="3">
</SubMenuNames>
</MenuInformation>
<SliderInformation>
<SliderImageAddress>..\..\Img\Hydrangeas.jpg,</SliderImageAddress>
<SliderImageAddress>..\..\Img\Jellyfish.jpg,</SliderImageAddress>
<SliderImageAddress>..\..\Img\Koala.jpg,</SliderImageAddress>
<SliderImageAddress>..\..\Img\Lighthouse.jpg,</SliderImageAddress>
<SliderImageAddress>..\..\Img\Penguins.jpg,</SliderImageAddress>
<SliderImageAddress>..\..\Img\Tulips.jpg,</SliderImageAddress>
</SliderInformation>
<LastProductInformation>
<Product Name="147">
<Subject>
</Subject>
<ProductImageAddress>http://localhost:1209/ckeditor/plugins/imagebrowser/browser/Hydrangeas.jpg</ProductImageAddress>
<ProductDes>
<p><span style="color:#FFA07A;">qwqweqweqe</span>qwe</p>
<p><span style="font-size:11px;">qweqweqw</span>e</p>
</ProductDes>
</Product>
<Product Name="dsa">
<Subject>salm</Subject>
<ProductImageAddress>http://localhost:1209/ckeditor/plugins/imagebrowser/browser/Hydrangeas.jpg</ProductImageAddress>
<ProductDes>
<p>sdADASDASDASDASDASDASD</p>
<p>ASDASDASDADASDASDASDASDA</p>
<p>ASDASDASDASDASDASDASDASDASD</p>
</ProductDes>
</Product>
</LastProductInformation>
</AriaGostarInformation>
I want select last product node in LastProductInformation and get this node's attribute.
My code is:
XmlDocument xdoc = new XmlDocument();
xdoc.Load(AppDomain.CurrentDomain.BaseDirectory + #"\static\css\xml\data.xml");
XmlNode xparent = xdoc.SelectSingleNode("//LastProductInformation");
var b = xparent.SelectSingleNode("/Product[last()]").Attributes["Name"].Value;
but this returns null. What should I do?
Using LINQ to XML
var value = XDocument.Load("path")
.Descendants("Product")
.Last()
.Attribute("Name").Value;
Also you can use XPath with LINQ to XML
var value = XDocument.Load("path")
.XPathSelectElement("//LastProductInformation/Product[last()]")
.Attribute("Name").Value;
Note: Make sure you have a reference to System.Xml.Linq namespace from your project.
You don't have to change to linq.
var b = xparent.SelectSingleNode("//Product")[last()].Attributes["Name"].Value;
The last() works like an index so should be at the end.

How to get a node from xml with c#- what am I doing wrong?

Namespaces and XML are still confusing the hell out of me.
Here is my XML (that comes from a SOAP request)
<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>
<MyResponse xmlns="http://tempuri.org/">
<OutputXML xmlns="http://tempuri.org/XMLSchema.xsd">
<Result>
<OutputXML>
<Result>
<Foo>
<Bar />
</Foo>
</Result>
</OutputXML>
</Result>
</OutputXML>
</MyResponse>
</soap:Body>
</soap:Envelope>
I am trying to extract the actual XML part from the SOAP response (starting with the Foo element):
var nsmgr = new XmlNamespaceManager(document.NameTable);
nsmgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
nsmgr.AddNamespace("", "http://tempuri.org/");
nsmgr.AddNamespace("", "http://tempuri.org/XMLSchema.xsd");
var xml = document.DocumentElement
.SelectSingleNode("Foo", nsmgr)
.InnerXml;
But SelectSingleNode returns null. I've tried some different variations on this but can't get anything working. What am I not understanding?
Try this one:
var nsmgr = new XmlNamespaceManager(document.NameTable);
nsmgr.AddNamespace("aaa", "http://tempuri.org/XMLSchema.xsd");
var xml = document.DocumentElement
.SelectSingleNode("aaa:Foo", nsmgr)
.InnerXml;
this is because of Default namespaces has no perfix.
You can use GetElementsByTagName to use namespace uri directly:
var xml = document.GetElementsByTagName("Foo",
"http://tempuri.org/XMLSchema.xsd")[0].InnerXml;
You can use LINQ to XML to get your result, also specify the namespace
XDocument document = XDocument.Load("test.xml");
XNamespace ns = "http://tempuri.org/XMLSchema.xsd";
var test = document.Descendants(ns + "Foo").FirstOrDefault();
Or if you don't want to specify NameSpace then:
var test2 = document.Descendants()
.Where(a => a.Name.LocalName == "Foo")
.FirstOrDefault();

SelectSingleNode always returns null?

Taking this simplifed example of my XML:
<?xml version="1.0"?>
<message xmlns="http://www.mydomain.com/MyDataFeed" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mydomain.com/MyDataFeed https://secure.mydomain/MyDataFeed/myDataFeed.xsd" requestId="13898" status="1">
<error>Invalid Login</error>
</message>
I am trying to select the 'error' node using SelectSingleNode method, however using the following code it always returns NULL?
XmlNode errorNode = oss.SelectSingleNode("/message/error");
if (errorNode != null)
Console.Writeline("There is an error");
From research I have done this is related to namespaces but I simply can't get anything to work. Any advice?
You're missing the XML namespace defined by the <message> node in your SelectSingleNode call. Assuming oss is an XmlDocument instance, you need to do this:
XmlNamespaceManager nsMgr = new XmlNamespaceManager(oss.NameTable);
nsMgr.AddNamespace("ns", "http://www.mydomain.com/MyDataFeed");
XmlNode errorNode = oss.SelectSingleNode("/ns:message/ns:error", nsMgr);
Marc

Categories

Resources