Get XML Child Node Value from soapxml - c#

From last couple of days I was trying to extract err:Errors from following XML using c#
I am using UPS webs services and when I cancel pickup I am getting this XML as return
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header />
<soapenv:Body>
<soapenv:Fault>
<faultcode>Client</faultcode>
<faultstring>An exception has been raised as a result of client data.</faultstring>
<detail>
<err:Errors xmlns:err="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1">
<err:ErrorDetail>
<err:Severity>Hard</err:Severity>
<err:PrimaryErrorCode>
<err:Code>9510131</err:Code>
<err:Description>Order has already been canceled</err:Description>
</err:PrimaryErrorCode>
</err:ErrorDetail>
</err:Errors>
</detail>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>
but cannot find any relevant solution.
This is what I tried:
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(strResponse);
XmlNamespaceManager xmlnsManager = new XmlNamespaceManager(xDoc.NameTable);
xDoc.SelectSingleNode(
"soapenv:Envelope/soap‌​env:Body/soapenv:Fault/err:ErrorDetail",
xmlnsManager).InnerText;
It looks like the SelectSingleNode returns nothing.

You have to add the namespaces to your NamespaceManager:
xmlnsManager.AddNamespace("soapenv","http://schemas.xmlsoap.org/soap/envelope/");
xmlnsManager.AddNamespace("err","http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1");
xDoc.SelectSingleNode(
"/soapenv:Envelope/soap‌​env:Body/soapenv:Fault/err:ErrorDetail",
xmlnsManager).InnerText;
before you call SelectSingleNode. Make sure you synchronize the namespace aliases in your namespacemanager with the alias used in your XPATH expression.

Related

How to get list of values when XML contains many namespaces

I have following XML:
<?xml version="1.0" encoding="utf-16"?>
<cincinnati xmlns="http://www.sesame-street.com/abc/def/1">
<cincinnatiChild xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ElementValue xmlns:a="http://schemas.data.org/2004/07/sesame-street.abc.def.ghi">
<a:someField>false</a:someField>
<a:data xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<b:KeyValueThing>
<b:Key>key1</b:Key>
<b:Value i:type="a:ArrayOfPeople">
<a:Person>
<a:firstField>
</a:firstField>
<a:dictionary>
<b:KeyValueThing>
<b:Key>ID</b:Key>
<b:Value i:type="c:long" xmlns:c="http://www.w3.org/2001/XMLSchema">000101</b:Value>
</b:KeyValueThing>
<b:KeyValueThing>
<b:Key>Name</b:Key>
<b:Value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">John</b:Value>
</b:KeyValueThing>
</a:dictionary>
</a:Person>
<a:Person>
...
<b:Value i:type="c:long" xmlns:c="http://www.w3.org/2001/XMLSchema">000102</b:Value>
...
</a:Person>
</b:Value>
</b:KeyValueThing>
</a:data>
</ElementValue>
</cincinnatiChild>
</cincinnati>
I need to get a list if ID values, e.g. 000101, 000102....
I think using XPath makes sense here but the multitude of namespaces makes it confusing (so a simple XmlNamespaceManager won't do).
Basically I need something like this (this syntax is of course not correct):
XmlDocument doc = // Load the xml
doc.DocumentElement.SelectSingleNode("/cincinati/cincinnatiChild/ElementValue/a:data/b:KeyValueThing/b:Value/a:Person/a:dictionary[b:KeyValueThing/b:Key='ID']");
also when I do doc.DocumentElement.SelectSingleNode("/cincinnati") or doc.DocumentElement.SelectSingleNode("/cincinnatiChild") I get null.
Since I'm unsure how to piece together al the helpfull advice from the comments I would like to see a working c# code line, either XmlDocument or XDocument are OK.
I also tries this before the SelectSingleNode:
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("a", "http://schemas.data.org/2004/07/sesame-street.abc.def.ghi");
nsmgr.AddNamespace("b", "http://schemas.microsoft.com/2003/10/Serialization/Arrays");
nsmgr.AddNamespace("c", "http://www.w3.org/2001/XMLSchema");
nsmgr.AddNamespace("i", "http://www.w3.org/2001/XMLSchema-instance");
nsmgr.AddNamespace("d", "http://www.sesame-street.com/abc/def/1");
You could bypass the namespace issues using the local-name()-function.
i.e.: in stead of b:KeyValueThing use *[local-name()='KeyValueThing']
a simple global XPath could look like this:
//*[local-name()='KeyValueThing'][*[local-name()='Key' and text()='ID' ]]/*[local-name()='Value']/text()
If you want to be more precise and speed up the XPath it would look like this:
/*[local-name()='cincinnati']/*[local-name()='cincinnatiChild']/*[local-name()='ElementValue']/*[local-name()='data']/*[local-name()='KeyValueThing']/*[local-name()='Value']/*[local-name()='Person']/*[local-name()='dictionary']/*[local-name()='KeyValueThing'][*[local-name()='Key' and text()='ID' ]]/*[local-name()='Value']/text()

Getting child nodes in XML with custom Namespace

I have a rather large XML file from a computer diagnostics session, and my goal is to grab the test results data and pump it into a PDF for the customer. I've very little experience with XML and this is turning out to be a huge problem.
Here is a sample of the Document:
<pcd:DiagLog xmlns="http://www.pc-doctor.com/2004/8/diagLogger"
xmlns:pcd="http://www.pc-doctor.com/2004/8/diagLogger"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.pc-doctor.com/2004/8/diagLogger
http://www.pc-doctor.com/2004/8/diagLogger/diagLogger.xsd">
<Application>
<version>6.0.6818.10</version>
<Start>
<Time hour="04" minute="14" second="01" millisecond="34" month="10" day="15" year="2016" utcOffset="-480">2016-10-15T04:14:01.034-08:00</Time>
</Start>
<OS>Windows 10 Service Pack 0 PE x86 32-bit</OS>
</Application>
.......
<DiagInfo>
....
<TestResult EnglishResult="PASS">
....
</TestResult>
</DiagInfo>
There are thousands of lines between </Application> and <DiagInfo>, but I'm only concerned with the information found in <DiagInfo> and <TestResult>.
I thought I could grab the Nodes by simply:
XmlDocument doc = new XmlDocument();
doc.Load(xmlFilePath);
XmlNamespaceManager manager = new XmlNamespaceManager(doc.NameTable);
manager.AddNamespace("pcd", "http://www.pc-doctor.com/2004/8/diagLogger");
XmlNodeList xnList = doc.SelectNodes("/pcd:DiagLog/DiagInfo", manager);
But this is returning an empty list. When I refer to Namespace Manager or XsltContext needed, it appears I'm doing it right, but I don't think I'm understanding adding a namespace correctly. When I change the Root Element to just: <Diagnostics></Diagnostics> instead of the <pcd:DiagLog>, and try: doc.SelectNodes("/Diagnostics/DiagInfo", manager); my nodes list is populated.
Can anyone see where I'm screwing up the Namespace?
You need to use the namespace prefix for all nodes in that namespace.
This is incorrect: /pcd:DiagLog/DiagInfo.
This is correct: /pcd:DiagLog/pcd:DiagInfo.

How to get resumptiontoken value from given xml

I am getting an XML from a OAI-PMH request which contains a resumption token to get next set of records. How to get the resumption token Value and its other attributes such as completeListSize etc.
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="static/style.xsl"?><OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd"><responseDate>2015-06-24T16:45:25Z</responseDate>
<request verb="ListRecords" metadataPrefix="uketd_dc">http://publications.iadb.org/oai/request</request>
<ListRecords>
<record>
<header>
<identifier>oai:publications.iadb.org:11319/195</identifier>
<datestamp>2015-06-12T23:02:48Z</datestamp>
<setSpec>com_123456789_1</setSpec>
<setSpec>col_123456789_3</setSpec>
</header>
<metadata></metadata>
</record>
<resumptionToken completeListSize="6305" cursor="0">MToxMDB8Mjp8Mzp8NDp8NTp1a2V0ZF9kYw==</resumptionToken>
</ListRecords>
I tried the below code
XDocument root= XDocument .Load("http://publications.iadb.org/oai/request?verb=ListRecords&metadataPrefix=uketd_dc");
var tokenValue= root.Element("resumptionToken").Value;
Its returning object reference error.Please help.
Element returns an immediate child element. From the context of your document, the only element available to you is OAI-PMH - this is why you're getting the null reference exception.
In addition, your target element has a namespace as defined by the default namespace declaration (xmlns="...") in the root OAI-PMH element.
You can use Descendants to find any element in the document with your required name, so this short code should work:
XNamespace ns = "http://www.openarchives.org/OAI/2.0/";
var tokenValue = (string)root.Descendants(ns + "resumptionToken").Single();

Lost prefix's definitions after deserialization of WCF message

I develop WCF-client. My client should validate incoming messages.
One of the messages has the following structure:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Header>...</SOAP-ENV:Header>
<SOAP-ENV:Body>
<OpDescriptionResponse>
<Field Name="DateTime" Type="xsd:dateTime">
</OpDescriptionResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
In this case the client should validate: the field "DateTime" has type "dateTime" from namespace "http://www.w3.org/2001/XMLSchema".
This response is deserialized in structure containing array of XmlElement.
But I have an issue: after message is deserialized and I received corresponding variable, containing all Field nodes I can't determine value of prefix "xsd", i.e. if I take any elementof type XMLElement corresponding to Field node in reply and call element.GetNamespaceOfPrefix("xsd") I get empty string as result.
How can I get prefix's definitions are saved after deserialization?
Help me please to overcome this issue.
In order to influence the namespace/prefix, you need to use XmlSerializerNamespaces.
The following code provides a rough reference:
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add("prefixHere", "http://namespace.here/");
XmlSerializer tempSerializer = new XmlSerializer(messageObject.GetType());
tempSerializer.Serialize(Console.Out, messageObject, namespaces);
Regards,

Namespace Manager or XsltContext needed

I have the following xml;
<env:Envelope xmlns:env='http://schemas.xmlsoap.org/soap/envelope/'>
   <env:Header>
       <mm7:TransactionID xmlns:mm7='http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-6-MM7-1-4' env:mustUnderstand='1'>6797324d</mm7:TransactionID>
   </env:Header>
   <env:Body>
       <DeliveryReportReq xmlns='http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL-6-MM7-1-4'>
           <MM7Version>6.8.0</MM7Version><MMSRelayServerID>TARAL</MMSRelayServerID>
<MessageID>T*3*T\*4\*855419761</MessageID>
<Recipient>
<RFC2822Address>+61438922562/TYPE=hidden</RFC2822Address>
</Recipient>
<Sender>
<RFC2822Address>61418225661/TYPE=hidden</RFC2822Address>
</Sender>
<Date>2011-08-15T12:57:27+10:00</Date>
<MMStatus>Retrieved</MMStatus>
<StatusText>The message was retrieved by the recipient</StatusText>
</DeliveryReportReq>  
</env:Body>
</env:Envelope>
So then i have the following c# code;
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(file);
XmlNode xNode = xDoc.SelectSingleNode("env:Envelope");
and i get the error;
Namespace Manager or XsltContext needed. This query has a prefix, variable, or user-defined function.
anyone know how to fix this?
Personally I would use LINQ to XML instead - its namespace support is far easier to get a handle on. It's not clear why you want to use XPath here anyway, given that Envelope is simply the root node - why not just ask for the root node?
However, if you really want to use XPath, you can create a new XmlNamespaceManager from the name table in the XmlDocument, register a prefix and then pass in the namespace manager to the SelectSingleNode overload which takes one.
There's some sample code in this answer but again I'd strongly urge you to consider other approaches if you can... particularly using LINQ to XML, where a search for (say) all the "env:Body" elements (only one here, but...) would look like this:
XNamespace env = "http://schemas.xmlsoap.org/soap/envelope/";
var bodies = doc.Descendants(env + "Body");

Categories

Resources