Linq-to-XML with XDocument namespace issue - c#

I have this XML document:
<?xml version="1.0" encoding="utf-8"?>
<directoryresponse xmlns="https://www.sisow.nl/Sisow/REST" version="1.0.0">
<directory>
<issuer>
<issuerid>01</issuerid>
<issuername>ABN Amro Bank</issuername>
</issuer>
<issuer>
<issuerid>02</issuerid>
<issuername>ASN Bank</issuername>
</issuer>
</directory>
</directoryresponse>
And this does not work:
var banks = doc.Descendants("issuer").Select(x =>
new Bank(Convert.ToInt32(x.Element("issuerid").Value), x.Element("issuername").Value)).ToList();
But when I manual remove the directoryresponse xml namespace
xmlns="https://www.sisow.nl/Sisow/REST" it works! The namespace url is a 404. So why doesn't the xdoc ignore the namespace if it's a 404?
This also does not work: var banks = doc.Elements().Where(e => e.Name.LocalName == "issuer" ).Select(...
The main question is: how can I modify my code so that it ignores the 404 namespace?

The URL itself is irrelevant here - it's just a token for the namespace, really. I don't believe LINQ to XML will try to fetch it.
However, you need to use it to construct the XName to search for:
XNamespace ns = "https://www.sisow.nl/Sisow/REST";
var banks = doc.Descendants(ns + "issuer")
.Select(x => new Bank((int) x.Element(ns + "issuerid"),
(string) x.Element(ns + "issuername"))
.ToList();

Related

Simplest way to get XML nodes with namespace?

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;

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.

Query XML node with namespace

My goal is to be able to retrieve the value of <Cdtr><Id> from this XML file:
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:com.firmaname:response.002">
<DocId>1261076951</DocId>
<CreateDtTm>2016-11-23T14:53:23.938Z</CreateDtTm>
<ResponseCd>OK</ResponseCd>
<Dbtr>
<Id>debtorId</Id>
<Name>debtorName</Name>
</Dbtr>
<Cdtr>
<Id>creditorId</Id>
<Name>creditorName</Name>
</Cdtr>
</Document>
Note that this file 2 <Id> nodes, so I must specify the exact path to the node. The result should be 'creditorId'.
My code is:
XNamespace ns = "urn:com.firmaname:response.002";
var results = requestMessage.Descendants(ns + "Id").First().Value;
But this will return 'debtorId'. I've searched here to find a way to retrieve an exact path, but they all seem to involve using Xpath with XElement. When using XElement I run into trouble with the namespace. All of the suggestions I find are about using XDocument...
I would still use XDocument and select the Cdtr node, then the Id node within it.
One hack you can do with namespaces if you are confident you don't need them is to use <element>.Name.LocalName.
XDocument doc = XDocument.Load("<path to xml");
XNamespace ns = "urn:com.firmaname:response.002";
var creditorid = doc.Descendants().Elements()
.Where(e => e.Name == ns + "Cdtr").Elements()
.First(e => e.Name == ns + "Id");
Update using LocalName
var creditorid = doc.Descendants().Elements()
.Where(e => e.Name.LocalName == "Cdtr").Elements()
.First(e => e.Name.LocalName == "Id");
This works, however you should use the namespace if you can.

how to get the namespaces in the root of xml

I have an xml whose root has namespaces like
<root version="2.0" xsi:schemaLocation="http://www.sample.org/schemas/2009 http://www.sample1.org/schemas/2009/railML-2.0/railML.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dc="http://xyz/elements/1.1/" xmlns="http://www.abcd.org/schemas/2009">
I am able to retrieve the value of xmlns using
var xdoc = XDocument.Load(XmlToParse);
Console.WriteLine(xdoc.Root.Name.NamespaceName);
but how can i get the values of other namespaces in root i.e. value of
xsi
dc
schemaLocation
Before parsing I need to verify these namespaces so i need these values.
how to do it?
can it be done by linq how?
var q = xdoc.Root.Attributes()
.Where(x => x.IsNamespaceDeclaration)
.Select(x => new {Prefixes = x.Name.LocalName, ns = x.Value});
EDIT:
More Ways: Get namespaces from an XML Document with XPathDocument and LINQ to XML

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

Categories

Resources