Namespace Manager or XsltContext needed - c#

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

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.

Get XML Child Node Value from soapxml

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.

Parsing XFDL Contents - C#

I am tasked with ripping and stripping pertinent data from XFDL files. I am attempting to use XmlDocument's SelectSignleNode method to do so. However, it has proven unsuccessful.
Represntative XML:
<XFDL>
...
<page1>
<check3>true</check3>
</page1>
...
<page sid="PAGE1">
<check sid="CHECK9">
<value>true</value>
</check>
</page>
...
Code:
XmlDocument document = new XmlDocument();
document.Load(memoryStream);//decoded and unzipped xfdl file
//Doesn't work
XmlNode checkBox = document.SelectSingleNode("//check[#sid='CHECK9']/value");
//Doesn't work
XmlNode checkBox = document.SelectSingleNode("//page[#sid='PAGE1']/check[#sid='CHECK9']");
MsgBox(checkBox.InnerXml);
Yields me System.NullReferenceException as an XmlNode isn't selected.
I think I'm having an xpath issue but I can't seem to understand where. The earlier xml node is easily selected using:
XmlNode checkBox = document.SelectSingleNode("//page1/check3");
MsgBox(checkBox.InnerText);
Displays just fine. And just to head it off at the pass, there isn't a definition of <check9></check9> in the <page1> tag.
Anyone have some insight?
Thanks in advance.
Okay, so here's the deal. XFDL defines a default namespace that requires an arbitrary mapping for xpath querying. In my case:
XML:
<XFDL xmlns="http://www.ibm.com/xmlns/prod/xfdl/8.0" ... >
Code:
manager.AddNamespace("a", "http://www.ibm.com/xmlns/prod/xfdl/8.0");
//Append 'a:' to query elements
document.SelectSingleNode("//a:check[#sid='CHECK9']/a:value", manager);
The problem is compounded because <check> is buried in <page> which is defined in another namespace: xfdl. My xpath query becomes:
document.SelectSingleNode("//xfdl:page[#sid='PAGE1']/a:check[#sid='CHECK9']/a:value", manager);
Now this is rather XFDL specific but can be applied to other issues where there are multiple namespaces defined within an XML document.
EDIT 1
Source: http://codeclimber.net.nz/archive/2008/01/09/How-to-query-a-XPath-doc-that-has-a-default.aspx

Why my XPath request to XML file on web doesn't work?

There is an XML file with exchange rates http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml:
<gesmes:Envelope>
<gesmes:subject>Reference rates</gesmes:subject>
<gesmes:Sender>
<gesmes:name>European Central Bank</gesmes:name>
</gesmes:Sender>
<Cube>
<Cube time="2009-11-26">
<Cube currency="USD" rate="1.5071"/>
...
I do next XPath request:
var doc = new XmlDocument();
doc.Load(url);
var node = doc.SelectSingleNode("//Cube[#currency=\"USD\""]);
var value = node.Attributes["rate"].Value;
but it returns null! Where is my mistake?
If I do this request, all works fine:
var node = dic.SelectSingleNode("//*[#currency=\"USD\"]");
var name = node.Name; // "Cube"
The problem is the namespace. If you can use LINQ to XML, you can express this query quite easily. Otherwise, it slightly trickier - you'd want something like this:
var doc = new XmlDocument();
doc.Load(url);
XPathNavigator navigator = doc.CreateNavigator();
XmlNamespaceManager nsMgr = new XmlNamespaceManager(nav.NameTable);
nsMgr.AddNamespace("gesmes", "http://www.gesmes.org/xml/2002-08-01");
nsMgr.AddNamespace("ns0", "http://www.ecb.int/vocabulary/2002-08-01/eurofxref");
var node = doc.SelectSingleNode("//ns0:Cube[#currency=\"USD\""], nsMgr);
var value = node.Attributes["rate"].Value;
(You don't really need the gesmes namespace in the manager there, but it'll make it easier if you need to look up any other elements.)
EDIT: Peter Murray-Rust's answer is a nice alternative here - which approach you take depends on how specific you want to be able the element to find. If you only need the namespace for one query, it makes sense to include the URI directly in the XPath; if you'll need it for more than that, you'll get more concise queries using the namespace manager.
Try
var node = doc.SelectSingleNode("//*[local-name()='Cube' and #currency=\"USD\""]);
you can always add in the namespace if you know it
var node = doc.SelectSingleNode("//*[local-name()='Cube' and
namespace-uri()='http://www.ecb.int/vocabulary/2002-08-01/eurofxref' and
#currency=\"USD\""]);
Although it's longwinded I prefer it to trying to sort out namespace prefixes. And it also avoids the problem of the default namespace (xmlns="")
XPathVisualizer can be handy. It's free. It wouldn't have told you to use the namespace, but it would have put the namespace in front of you in the UI, and it would havve let you test a bunch of alternatives, really quickly.

Categories

Resources