Selecting XML Node with XPath - c#

I have a xml where i want to select a node from it here is the xml:
<?xml version="1.0" encoding="utf-8" ?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<InResponse xmlns="https://ww.ggg.com">
<InResult>Error </InResult>
</InResponse>
</soap:Body>
</soap:Envelope>
I am loading it using XmlDocument's LoadXML and trying to get InResult node but I get null see below please:
xml.SelectSingleNode("//InResult").InnerText;

You have a namespace declaration and you should add this into your XPath or you can use namespace agnostic XPath. Try next code as namespace agnostic solution:
xml.SelectSingleNode("//*[local-name()='InResult']").InnerText;
I've received Error as result
From http://www.w3schools.com/ site:
local-name() - Returns the name of the current node or the first node
in the specified node set - without the namespace prefix
You can get more information about XPath functions here.
Namespace aware solution, is given below:
var namespaceManager = new XmlNamespaceManager(x.NameTable);
namespaceManager.AddNamespace("defaultNS", "https://ww.ggg.com");
var result = x.SelectSingleNode("//defaultNS:InResponse", namespaceManager).InnerText;
Console.WriteLine (result); //prints Error
Brief XML notes:
This part in root note xmlns:soap="http://www.w3.org/2003/05/soap-envelope" is a xml namespace declaration. It is used to identify nodes in your xml structure. As a rule, you need to specify them to access nodes with it, but there are namespace agnostic solutions in XPath and in LINQ to XML. Now if you see node name as <soap:Body>, this means, that this node belongs to this namespace.

This seems to be an namespace issue
You can use an XmlNamespaceManager before you call SelectSingleNode():
XmlNamespaceManager ns = new XmlNamespaceManager(xmldoc.NameTable);
ns.AddNamespace("ggg", "https://ww.ggg.com");
xml.SelectSingleNode("//ggg:InResult", ns).InnerText;
Attention: Not tested.

Related

Error in Xml parsing with C# XmlDocument class

When I add xmls attribute to my root element this code through a exception at third line " Object reference not set to an instance of an object" but after removing xmls attribute from root element it it works fine.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("file.xml");
MessageBox.Show(xmlDoc.SelectSingleNode("person/name").InnerText);
here is my xmlfile
<?xml version="1.0" encoding="utf-8"?>
<person xmlns="namespace path">
<name>myname</name>
</person>
I want to know why it does not works after adding xmlns attribute to my root element. Do I have to use another method for parsing ?.
You need to add namespace messenger to resolve namespaces to your xml file.
Consider this example
XML File
<?xml version="1.0" encoding="utf-8"?>
<person xmlns="http://www.findpersonName.com"> // Could be any namespace
<name>myname</name>
</person>
and in your code
XmlDocument doc = new XmlDocument();
doc.Load("file.xml");
//Create an XmlNamespaceManager for resolving namespaces.
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("ab", "http://www.findpersonName.com");
MessageBox.Show(doc.SelectSingleNode("//ab:name", nsmgr).InnerText);
Note
If the XPath expression does not include a prefix, it is assumed
that the namespace URI is the empty namespace. If your XML includes a
default namespace, you must still add a prefix and namespace URI to
the XmlNamespaceManager; otherwise, you will not get a node selected.
For more information, see Select Nodes Using XPath Navigation.
XmlNamespaceManager ns = new XmlNamespaceManager(xmldoc.NameTable);
ns.AddNamespace("something", "http://or.other.com/init");
XmlNode node = xmldoc.SelectSingleNode("something:person/name", ns);
You may want to consider using XDocument and Linq to process your XML document.
The following example provides a rough example:
XDocument xDoc = XDocument.Load("file.xml");
var personNames = (from x in xDoc.Descendants("person").Descendants("name") select x).FirstOrDefault();
How to Get XML Node from XDocument

How do you refer to an xml node that has two namespace definitions?

I have an xml message from a 3rd party that has a node:
<ClinicalDocument xmlns="urn:hl7-org:v3" xmlns:npfitlc="NPFIT:HL7:Localisation"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
classCode="DOCCLIN" moodCode="EVN">
I created a Namespace object to use to identify npfitlc items under this node:
ns.AddNamespace("npfitlc", "NPFIT:HL7:Localisation");
But when I try to choose the ClinicalDocument node it can't find it:
XmlNode myNode = soapEnvelop.SelectSingleNode
("//soap:Envelope/soap:Body/itk:DistributionEnvelope/itk:payloads/itk:
payload/ClinicalDocument", ns);
As you can see in my doc there are multiple nodes to get to Clinical Document. And when I reference down to itk:payload it locates it fine:
XmlNode myNode = soapEnvelop.SelectSingleNode
("//soap:Envelope/soap:Body/itk:DistributionEnvelope/itk:
payloads/itk:payload", ns);
I took out xmlns="urn:hl7-org:v3" from the ClinicalDocument tag and then I could find it find with my SelectSingleNode call, but the system I sent the message to fails validation because that is missing.
I am not sure how to handle it where there is a "root" namespace defined in that node.
ClinicalDocument has no prefix and it has an xmlns="urn:hl7-org:v3" namespace declaration, which means that its namespace is urn:hl7-org:v3. The rest of the namespace declarations there are completely irrelevant for the purpose of selecting this particular element.
So what you need to do is...
Add that namespace to your namespace manager (using any nonempty prefix):
ns.AddNamespace("hl", "urn:hl7-org:v3");
Use that prefix in your XPath:
XmlNode myNode =
soapEnvelop.SelectSingleNode("//soap:Envelope/soap:Body" +
"/itk:DistributionEnvelope/itk:payloads" +
"/itk:payload/hl:ClinicalDocument", ns);
and that should do it.

Namespace of specific XML Node in c#

I have the following XML structure:
<?xml version="1.0" encoding="utf-16"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance
" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<StoreResponse xmlns="http://www.some-site.com">
<StoreResult>
<Message />
<Code>OK</Code>
</StoreResult>
</StoreResponse>
</soap:Body>
</soap:Envelope>
I need to get the InnerText from Codeout of this document and I need help with the appropriate XPATH statement.
I'm really confused by XML namespaces. While working on a previous namespace problem in another XML document, I learned, that even if there's nothing in front of Code (e.g. ns:Code), it is still part of a namespace defined by the xmlns attribute in its parent node. Now, there are multiple xmlns nodes defined in parents of Code. What is the namespace that I need to specify in an XPATH statement? Is there such a thing as a "primary namespace"? Do childnodes inherit the (primary) namespace of it's parents?
The namespace of the <Code> element is http://www.some-site.com. xmlsn:xxx means that names prefixed by xxx: (like soap:Body) have that namespace. xmlns by itself means that this is the default namespace for names without any prefix.
An example of using an XDocument (Linq) approach:
XNamespace ns = "http://www.some-site.com";
var document = XDocument.Parse("your-xml-string");
var elements = document.Descendants( ns + "StoreResult" )
Descendant elements will inherit the last immediate namespace. In your example you will need to create two namespaces one for the soap envelope and a second for "some-site".
Here's an option I found in this question: Weirdness with XDocument, XPath and namespaces
var xml = "<your xml>";
var doc = XDocument.Parse(xml); // Could use .Load() here too
var code = doc.XPathSelectElement("//*[local-name()='Code']");

Process namespaces using XmlReader

I have a complex XML file with structure as follows:
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="xxx:xxx:xxx:xxx:xxxxx:xxx:xsd:xxxx.xxx.xxx.xx">
<Element1>
<Element2>
<Element2A>xxxxxx</Element2A>
<Element2B>2012-08-29T00:00:00</Element2B>
</Element2>
</Element1>
</Document>
Now I am using XmlReader to read this XML document and process information as follows
XmlReader xr = XmlReader.Create(filename);
while (xr.Read())
{
xr.MoveToElement();
XElement node = (XElement)XElement.ReadFrom(xr);
Console.WriteLine(node.Name);
}
xr.Close();
The problem I am facing is in the output the namespace is prefixed to the ElementName. E.g output
{xxx:xxx:xxx:xxx:xxxxx:xxx:xsd:xxxx.xxx.xxx.xx}Element1
Is there any way I can remove/ handle this as I need to do further filtering using Element names and Child names.
XElement.Name is not (as you might expect) a String, but rather an XName which has a LocalName property, thus:
Console.WriteLine(node.Name.LocalName);
You may want to remove the namespace. One way to remove namespace is to write c# code and other way is to use XSLT transformation as suggested in Remove Namespace
-Milind

Query an XmlDocument without getting a 'Namespace prefix is not defined' problem

I've got an Xml document that both defines and references some namespaces. I load it into an XmlDocument object and to the best of my knowledge I create a XmlNamespaceManager object with which to query Xpath against. Problem is I'm getting XPath exceptions that the namespace "my" is not defined. How do I get the namespace manager to see that the namespaces I am referencing are already defined. Or rather how do I get the namespace definitions from the document to the namespace manager.
Furthermore tt strikes me as strange that you have to provide a namespace manager to the document which you create from the documents nametable in the first place. Even if you need to hardcode manual namespaces why can't you add them directly to the document. Why do you always have to pass this namespace manager with every single query? What can't XmlDocument just know?
Code:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(programFiles + #"Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\HfscBookingWorkflow\template.xml");
XmlNamespaceManager ns = new XmlNamespaceManager(xmlDoc.NameTable);
XmlNode referenceNode = xmlDoc.SelectSingleNode("/my:myFields/my:ReferenceNumber", ns);
referenceNode.InnerXml = this.bookingData.ReferenceNumber;
XmlNode titleNode = xmlDoc.SelectSingleNode("/my:myFields/my:Title", ns);
titleNode.InnerXml = this.bookingData.FamilyName;
Xml:
<?xml version="1.0" encoding="UTF-8" ?>
<?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:Inspection:-myXSD-2010-01-15T18-21-55" solutionVersion="1.0.0.104" productVersion="12.0.0" PIVersion="1.0.0.0" ?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<my:myFields xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-01-15T18:21:55" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003">
<my:DateRequested xsi:nil="true" />
<my:DateVisited xsi:nil="true" />
<my:ReferenceNumber />
<my:FireCall>false</my:FireCall>
Update:
ns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
ns.AddNamespace("xhtml", "http://www.w3.org/1999/xhtml");
ns.AddNamespace("xd", "http://schemas.microsoft.com/office/infopath/2003");
ns.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-01-15T18:21:55");
This does the job, but it mean's I have to hard code to this particular xml schema. This schema represents an infopath form template. In particular the my namespace url will be different for every form template so I really don't want to hardcode this. It would be nice to find a clean way to get this namespace from the xml without resorting to RegEx.
I was hoping that the XmlNamespaceManager would just sort of pick up the namespace definitions form the NameTable. I mean their in the Xml but I still have to define them.
ns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
ns.AddNamespace("xhtml", "http://www.w3.org/1999/xhtml");
ns.AddNamespace("xd", "http://schemas.microsoft.com/office/infopath/2003");
ns.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-01-15T18:21:55");
This does the job, but it mean's I have to hard code to this particular xml schema. This schema represents an infopath form template. In particular the my namespace url will be different for every form template so I really don't want to hardcode this. It would be nice to find a clean way to get this namespace from the xml without resorting to Regex.
I was hoping that the XmlNamespaceManager would just sort of pick up the namespace definitions form the NameTable. I mean their in the Xml but I still have to define them.
Here is the answer to the "What can't XmlDocument just know?" question.
NameTable is just an optimization for storing names. It has actually nothing to do with namespaces.
And even if XmlNamespaceManager could infer all namespaces and prefixes from XML doc that won't help in general case because of XML namespaces nature, e.g. what would XmlNamespaceManager map "my" prefix in this case:
<root>
<foo xmlns:my="blah"/>
<foo xmlns:my="balh-blah-blah"/>
</root>
Have you defined "my" in the namespace-manager?
ns.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-01-15T18:21:55");
Or better - choose something that is unlikely to conflict. It does seem odd that it didn't pick it up from the name-table, though.
For me with InfoPath 2007 this solved the problem
static public XmlNamespaceManager GetNameSpaceManager(this XmlDocument document)
{
XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(document.NameTable);
xmlNamespaceManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
xmlNamespaceManager.AddNamespace("dfs", "http://schemas.microsoft.com/office/infopath/2003/dataFormSolution");
xmlNamespaceManager.AddNamespace("d", "http://schemas.microsoft.com/office/infopath/2003/ado/dataFields");
xmlNamespaceManager.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2012-03-29T06:28:28");
xmlNamespaceManager.AddNamespace("xd", "http://schemas.microsoft.com/office/infopath/2003");
return xmlNamespaceManager;
}

Categories