C# Xml Value always getting null - c#

I have the following Xml in my Resources.xmltest:
<?xml version="1.0" encoding="utf-8" ?>
<Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://DFISofft.com/SmartPayments/">
<Result>0</Result>
<Message>Pending</Message>
<PNRef>222131</PNRef>
<ExtData>InvNum=123</ExtData>
</Response>
I've tried several ways to get the values, Result,Message,PNRef,ExtData, out of it and I've had no luck. I always get a null value for the NodePath so it never goes into the loop:
var XmlDoc = new XmlDocument();
XmlDoc.LoadXml(Resources.xmltest);
XmlElement NodePath = (XmlElement) XmlDoc.SelectSingleNode("/Response");
while (NodePath != null)
{
foreach (XmlNode Xml_Node in NodePath)
{
Console.WriteLine(Xml_Node.Name + " " + Xml_Node.InnerText);
}
}
I've tried this:
XmlNode node3 = XmlDoc.SelectSingleNode("PNRef");
Console.WriteLine(node3.Value);
Console.WriteLine(XmlDoc.InnerXml);
var tst = XmlDoc.GetElementsByTagName("PNRef");
Console.WriteLine(tst);
And this:
NodePath = (XmlElement) XmlDoc.SelectSingleNode("/Response");
if (NodePath != null)
{
foreach (XmlNode node in NodePath)
{
Console.WriteLine("NodeName: " + Xml_NodeX.Name);
Console.WriteLine("NodeValue: " + node.InnerText);
}
}
Apparently, I'm not getting the xml read/write. I've done it with DataSets but they do all the work for you.
Can someone point me in the right direction? I've been at this for longer than I should have been already.
Thank you!

Your XML has a XML namespace and you're not paying any attention to it:
<Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://DFISofft.com/SmartPayments/">
******************************************
When selecting from this XML, you need to use that XML namespace!
You're not taking into account the XML namespace (xmlns="http://nts-de-osm1-pxc/webservices/") on the document!
Try this:
var XmlDoc = new XmlDocument();
// setup the XML namespace manager
XmlNamespaceManager mgr = new XmlNamespaceManager(XmlDoc.NameTable);
// add the relevant namespaces to the XML namespace manager
mgr.AddNamespace("ns", "http://DFISofft.com/SmartPayments/");
XmlDoc.LoadXml(Resources.xmltest);
// **USE** the XML anemspace in your XPath !!
XmlElement NodePath = (XmlElement) XmlDoc.SelectSingleNode("/ns:Response");
while (NodePath != null)
{
foreach (XmlNode Xml_Node in NodePath)
{
Console.WriteLine(Xml_Node.Name + " " + Xml_Node.InnerText);
}
}

The problem is your XPath query, which doesn't specify a namespace - despite the fact that your document only has a Response element in the "http://DFISofft.com/SmartPayments/" namespace. Ditto the PNRef elements.
Personally I'd use LINQ to XML if I actually wanted to use namespaces if at all possible. For example:
XNamespace ns = "http://DFISofft.com/SmartPayments/";
XDocument doc = XDocument.Load(filename);
foreach (var element in doc.Descendants(ns + "PNRef"))
{
// Use element here
}
As you can see, LINQ to XML makes it really easy to use namespaces - but of course it requires .NET 3.5 or higher. If I had to use .NET 2.0, I'd either use XmlNamespaceManager or iterate manually and check local names instead of using XPath.

That is because the node isn't called response; you need to take the namespace into account:
var XmlDoc = new XmlDocument();
var nsmgr = new XmlNamespaceManager(XmlDoc.NameTable);
nsmgr.AddNamespace("x", "http://DFISofft.com/SmartPayments/");
XmlDoc.LoadXml(yourXml);
XmlElement NodePath = (XmlElement)XmlDoc.SelectSingleNode("/x:Response", nsmgr);

For future reference...
Bubba Soft
This site has a great free tool for building XPath Expressions (XPath Builder).

It's because you are using the overload of SelectSingleNode that assumes an empty namespace. Since you have a default namespace you will need to use the version that takes an XmlNamespaceManager. See this article for more info.
from the docs:
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.

Related

Extracting XML Child Elements Where the Parents are in a Defaulted Namespace

I have the below XML and I've been trying to extract the FirstName, LastName and OtherName for a while now I'm running into all sort of problems.
<OmdCds xmlns="cds"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cdsd="cds_dt"
xsi:schemaLocation="cds ontariomd_cds.xsd">
<PatientRecord>
<Demographics>
<Names>
<cdsd:LegalName namePurpose="L">
<cdsd:FirstName>
<cdsd:Part>SARAH</cdsd:Part>
<cdsd:PartType>GIV</cdsd:PartType>
<cdsd:PartQualifier>BR</cdsd:PartQualifier>
</cdsd:FirstName>
<cdsd:LastName>
<cdsd:Part>GOMEZ</cdsd:Part>
<cdsd:PartType>FAMC</cdsd:PartType>
<cdsd:PartQualifier>BR</cdsd:PartQualifier>
</cdsd:LastName>
<cdsd:OtherName>
<cdsd:Part>GABRIELA</cdsd:Part>
<cdsd:PartType>GIV</cdsd:PartType>
<cdsd:PartQualifier>BR</PartQualifier>
I currently trying to extract with the below c# code but still can't extract the above data. I'm getting a nullreferenceexception.
XmlDocument doc = new XmlDocument();
doc.Load(folder + "\\" + o.ToString());
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(doc.NameTable);
namespaceManager.AddNamespace("cdsd", "http://www.w3.org/2001/XMLSchema-instance");
XmlNode firstName = doc.DocumentElement.SelectSingleNode("/PatientRecord/Demographics/Names/cdsd:LegalName/cdsd:FirstName/cdsd:Part", namespaceManager);
string fName = firstName.InnerText;
MessageBox.Show(fName);
I can see in the local watch item under doc.DocumentElement, all the InnerXML and InnerText. The InnerXML look something like this...
<PatientRecord xmlns=\"cds\"><Demographics><Names><cdsd:LegalName namePurpose=\"L\" xmlns:cdsd=\"cds_dt\"><cdsd:FirstName><cdsd:Part>SARAH</cdsd:Part><cdsd:PartType>GIV</cdsd:PartType><cdsd:PartQualifier>BR</cdsd:PartQualifier></cdsd:FirstName>
You have 3 namespace definitions in the document:
cds - as a default namespace
http://www.w3.org/2001/XMLSchema-instance- with the xsi prefix
cds_dt - with the cdsd prefix
I am wondering that you don't get an error message, because cds and cds_dt are no URIs and namspaces need to be URIs.
If you try to understand an element name you need to replaces the prefix with the actual namespace.
<PatientRecord> reads as {cds}:PatientRecord
<cdsd:LegalName> reads as {cds_dt}:LegalName
Now in XPath 1.0 the same happens with registered namespaces. But XPath does not have a default namespace. So elements without one are not expanded with a default namespace.
You need to register namespace prefixes on the namespace manager. The prefix does not need to be the same as in the document.
namespaceManager.AddNamespace("cdsd", "cds_dt");
namespaceManager.AddNamespace("cds", "cds");
Now you can use the registered namespaces in XPath:
doc.DocumentElement.SelectSingleNode(
"cds:PatientRecord/cds:Demographics/cds:Names/cdsd:LegalName/cdsd:FirstName/cdsd:Part",
namespaceManager
);
If the first character of an XPath expression is a slash the expression is relative to the document, otherwise to the current context node. You call SelectSingleNode() on the doc.DocumentElement - the OmdCds element node. PatientRecord is a child node, so you can start with it or use . for the current context node.
PatientRecord, Demographics and Names are in the cds namespace. This is because of the default namespace declaration on the OmdCds element (xmlns="cds"). The others are in the cdsd namespace, not xsi. You'll have to add them and use them in the XPATH:
namespaceManager.AddNamespace("cdsd", "cdsd");
namespaceManager.AddNamespace("cds", "cds");
XmlNode firstName = doc.DocumentElement.SelectSingleNode(
"/cds:PatientRecord/cds:Demographics/cds:Names/cdsd:LegalName/cdsd:FirstName/cdsd:Part",
namespaceManager);
BTW, you're getting a NullReferenceException because you're making the false assumption that your query will always return a node. You are now seeing what happens when it does not return a node. Always check for null whenever it's possible that a query returns no value.
Instead XmlDocument class you can use Linq to XML, is easy. You need using the System.Xml.Linq namspace, for example:
XDocument xdoc = XDocument.Load("path");
IEnumerable<XElement> nodes = (from p in xdoc.Descendants()
where p.Name.LocalName == "FirstName"
select p).Elements();
foreach (XElement nodeFirstName in nodes)
{
foreach (XElement parts in nodeFirstName.Elements())
{
string strExtracted = parts.Name.LocalName + " " + parts.Value;
}
}
The LocalName property is used beacuse elements have a prefix "cdsd"

Read first root node from XML

I work with three kinds of XML files :
Type A:
<?xml version="1.0" encoding="UTF-8"?>
<nfeProc versao="2.00" xmlns="http://www.portalfiscal.inf.br/nfe">
</nfeProc>
Tyepe B:
<?xml version="1.0" encoding="UTF-8"?>
<cancCTe xmlns="http://www.portalfiscal.inf.br/cte" versao="1.04">
</cancCTe>
Type C:]
<?xml version="1.0" encoding="UTF-8"?>
<cteProc xmlns="http://www.portalfiscal.inf.br/cte" versao="1.04">
</cteProc>
I have try with this code to read the first node :
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(#"C:\crruopto\135120068964590_v01.04-procCTe.xml");
XmlNodeList ml = xmlDoc.GetElementsByTagName("*");
XmlElement root = xmlDoc.DocumentElement;
exti = root.ToString();
but dont return anything i want to read the first node , need to know if the file is nfeProc ,canCTE or cteProc
The second question is how i get the value from "value" in the same tag???
Thanks
From this post:
//Root node is the DocumentElement property of XmlDocument
XmlElement root = xmlDoc.DocumentElement
//If you only have the node, you can get the root node by
XmlElement root = xmlNode.OwnerDocument.DocumentElement
I would suggest using XPath. Here's an example where I read in the XML content from a locally stored string and select whatever the first node under the root is:
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(xml));
XmlNode node = doc.SelectSingleNode("(/*)");
If you aren't required to use the XmlDocument stuff, then Linq is your friend.
XDocument doc = XDocument.Load(#"C:\crruopto\135120068964590_v01.04-procCTe.xml");
XElement first = doc.GetDescendants().FirstOrDefault();
if(first != null)
{
//first.Name will be either nfeProc, canCTE or cteProc.
}
Working with Linq to XML is the newest and most powerful way of working with XML in .NET and offers you a lot more power and flexibility than things like XmlDocument and XmlNode.
Getting the root node is very simple:
XDocument doc = XDocument.Load(#"C:\crruopto\135120068964590_v01.04-procCTe.xml");
Console.WriteLine(doc.Root.Name.ToString());
Once you have constructed an XDocument you don't need to use any LINQ querying or special checking. You simply pull the Root property from the XDocument.
Thanks i have solved this way the first part
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(nomear);
XmlNodeList ml = xmlDoc.GetElementsByTagName("*");
XmlNode primer = xmlDoc.DocumentElement;
exti = primer.Name;
First, to be clear, you're asking about the root element, not the root node.
You can use an XmlReader to avoid having to load large documents completely into memory. See my answer to a how to find the root element at https://stackoverflow.com/a/60642354/1307074.
Second, once the reader is referencing the element, you can use the reader's Name property to get the qualified tag name of the element. You can get the value as a string using the Value property.

NodeList.SelectSingleNode() syntax

Having problems getting NodeList.SelectSingleNode() to work properly.
My XML looks like this:
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<inm:Results xmlns:inm="http://www.namespace.com/1.0">
<inm:Recordset setCount="18254">
<inm:Record setEntry="0">
<!-- snip -->
<inm:Image>fileName.jpg</inm:Image>
</inm:Record>
</inm:Recordset>
</inm:Results>
The data is a long series of <inm:Record> entries.
I open the doc and get create a NodeList object based on "inm:Record". This works great.
XmlDocument xdoc = new XmlDocument();
xdoc.Load(openFileDialog1.FileName);
XmlNodeList xRecord = xdoc.GetElementsByTagName("inm:Record");
I start looping through the NodeList using a for loop. Before I process a given entry, I want to check and see if the <inm:Image> is set. I thought it would be super easy just to do
string strImage = xRecord[i].SelectSingleNode("inm:Image").InnerText;
My thinking being, "For the XRecord that I'm on, go find the <inm:Image> value ...But this doesn't work as I get the exception saying that I need a XmlNameSpaceManager. So, I tried to set that up but could never get the syntax right.
Can someone show me how to use the correct XmlNameSpaceManager syntax in this case.
I've worked around the issue for now by looping through all of the childNodes for a given xRecord, and checking the tag once I loop around to it. I would like to check that value first to see if I need to loop over that <inm:Record> entry at all.
No need to loop through all the Record elements, just use XPath to specify the subset that you want:
XmlDocument xdoc = new XmlDocument();
xdoc.Load(openFileDialog1.FileName);
XmlNamespaceManager manager = new XmlNamespaceManager(xdoc.NameTable);
manager.AddNamespace("inm", "http://www.inmagic.com/webpublisher/query");
XmlNodeList nodes = xdoc.SelectNodes("/inm:Results/inm:Recordset/inm:Record[inm:Image != '']", manager);
Using the LINQ to XML libraries, here's an example for retrieving that said node's value:
XDocument doc = XDocument.Load(openFileDialog1.FileName);
List<XElement> docElements = doc.Elements().ToList();
XElement results = docElements.Elements().Where(
ele => ele.Name.LocalName == "Results").First();
XElement firstRecord = results.Elements().Where(
ele => ele.Name.LocalName == "Record").First();
XElement recordImage = firstRecord .Elements().Where(
ele => ele.Name.LocalName == "Image").First();
string imageName = recordImage.Value;
Also, by the way, using Hungarian notation for a type-checked language is overkill. You don't need to prepend string variables with str when it will always be a string.
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable);
string strImage = xRecord[i].SelectSingleNode("inm:Image",nsMgr).InnerText;
Should do it.
Using this Xml library, you can get all the records that have an Image child element with this:
XElement root = XElement.Load(openFileDialog1.FileName);
XElement[] records = root.XPath("//Record[Image]").ToArray();
If you want to be sure that the Image child contains a value, it can be expressed like this:
XElement[] records = root.XPath("//Record[Image != '']").ToArray();

XPath doesn't work as desired in C#

My code doesn't return the node
XmlDocument xml = new XmlDocument();
xml.InnerXml = text;
XmlNode node_ = xml.SelectSingleNode(node);
return node_.InnerText; // node_ = null !
I'm pretty sure my XML and Xpath are correct.
My Xpath : /ItemLookupResponse/OperationRequest/RequestId
My XML :
<?xml version="1.0"?>
<ItemLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2005-10-05">
<OperationRequest>
<RequestId>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx</RequestId>
<!-- the rest of the xml is irrelevant -->
</OperationRequest>
</ItemLookupResponse>
The node my XPath returns is always null for some reason. Can someone help?
Your XPath is almost correct - it just doesn't take into account the default XML namespace on the root node!
<ItemLookupResponse
xmlns="http://webservices.amazon.com/AWSECommerceService/2005-10-05">
*** you need to respect this namespace ***
You need to take that into account and change your code like this:
XmlDocument xml = new XmlDocument();
xml.InnerXml = text;
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xml.NameTable);
nsmgr.AddNamespace("x", "http://webservices.amazon.com/AWSECommerceService/2005-10-05");
XmlNode node_ = xml.SelectSingleNode(node, nsmgr);
And then your XPath ought to be:
/x:ItemLookupResponse/x:OperationRequest/x:RequestId
Now, your node_.InnerText should definitely not be NULL anymore!
Sorry for the late reply but I had a similar problem just a moment ago.
if you REALLY want to ignore that namespace then just delete it from the string you use to initialise the XmlDocument
text=text.Replace(
"<ItemLookupResponse xmlns=\"http://webservices.amazon.com/AWSECommerceService/2005-10-05\">",
"<ItemLookupResponse>");
XmlDocument xml = new XmlDocument();
xml.InnerXml = text;
XmlNode node_ = xml.SelectSingleNode(node);
return node_.InnerText;

SelectSingleNode returning null for known good xml node path using XPath

Consider this simple XML document. The serialized XML shown here is the result of an XmlSerializer from a complex POCO object whose schema I have no control over.
<My_RootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="">
<id root="2.16.840.1.113883.3.51.1.1.1" extension="someIdentifier" xmlns="urn:hl7-org:v3" />
<creationTime xsi:nil="true" xmlns="urn:hl7-org:v3" />
</My_RootNode>
The goal is to extract the value of the extension attribute on the id node. In this case, we are using the SelectSingleNode method, and given an XPath expression as such:
XmlNode idNode = myXmlDoc.SelectSingleNode("/My_RootNode/id");
//idNode is evaluated to null at this point in the debugger!
string msgID = idNode.Attributes.GetNamedItem("extension").Value;
The problem is that the SelectSingleNode method returns null for the given XPath expression.
Question: any ideas on this XPath query's correctness, or why this method call + XPath expression would return a null value? Perhaps the namespaces are part of the problem?
I strongly suspect the problem is to do with namespaces. Try getting rid of the namespace and you'll be fine - but obviously that won't help in your real case, where I'd assume the document is fixed.
I can't remember offhand how to specify a namespace in an XPath expression, but I'm sure that's the problem.
EDIT: Okay, I've remembered how to do it now. It's not terribly pleasant though - you need to create an XmlNamespaceManager for it. Here's some sample code that works with your sample document:
using System;
using System.Xml;
public class Test
{
static void Main()
{
XmlDocument doc = new XmlDocument();
XmlNamespaceManager namespaces = new XmlNamespaceManager(doc.NameTable);
namespaces.AddNamespace("ns", "urn:hl7-org:v3");
doc.Load("test.xml");
XmlNode idNode = doc.SelectSingleNode("/My_RootNode/ns:id", namespaces);
string msgID = idNode.Attributes["extension"].Value;
Console.WriteLine(msgID);
}
}
If you want to ignore namespaces completely, you can use this:
static void Main(string[] args)
{
string xml =
"<My_RootNode xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"\">\n" +
" <id root=\"2.16.840.1.113883.3.51.1.1.1\" extension=\"someIdentifier\" xmlns=\"urn:hl7-org:v3\" />\n" +
" <creationTime xsi:nil=\"true\" xmlns=\"urn:hl7-org:v3\" />\n" +
"</My_RootNode>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlNode idNode = doc.SelectSingleNode("/*[local-name()='My_RootNode']/*[local-name()='id']");
}
This should work in your case without removing namespaces:
XmlNode idNode = myXmlDoc.GetElementsByTagName("id")[0];
Sorry, you forgot the namespace. You need:
XmlNamespaceManager ns = new XmlNamespaceManager(myXmlDoc.NameTable);
ns.AddNamespace("hl7","urn:hl7-org:v3");
XmlNode idNode = myXmlDoc.SelectSingleNode("/My_RootNode/hl7:id", ns);
In fact, whether here or in web services, getting null back from an XPath operation or anything that depends on XPath usually indicates a problem with XML namespaces.
Just to build upon solving the namespace issues, in my case I've been running into documents with multiple namespaces and needed to handle namespaces properly. I wrote the function below to get a namespace manager to deal with any namespace in the document:
private XmlNamespaceManager GetNameSpaceManager(XmlDocument xDoc)
{
XmlNamespaceManager nsm = new XmlNamespaceManager(xDoc.NameTable);
XPathNavigator RootNode = xDoc.CreateNavigator();
RootNode.MoveToFollowing(XPathNodeType.Element);
IDictionary<string, string> NameSpaces = RootNode.GetNamespacesInScope(XmlNamespaceScope.All);
foreach (KeyValuePair<string, string> kvp in NameSpaces)
{
nsm.AddNamespace(kvp.Key, kvp.Value);
}
return nsm;
}
Well... I had the same issue and it was a headache. Since I didn't care much about the namespace or the xml schema, I just deleted this data from my xml and it solved all my issues. May not be the best answer? Probably, but if you don't want to deal with all of this and you ONLY care about the data (and won't be using the xml for some other task) deleting the namespace may solve your problems.
XmlDocument vinDoc = new XmlDocument();
string vinInfo = "your xml string";
vinDoc.LoadXml(vinInfo);
vinDoc.InnerXml = vinDoc.InnerXml.Replace("xmlns=\"http://tempuri.org\/\", "");
The rule to keep in mind is: if your document specifies a namespace, you MUST use an XmlNamespaceManager in your call to SelectNodes() or SelectSingleNode(). That's a good thing.
See the article Advantages of namespaces . Jon Skeet does a great job in his answer showing how to use XmlNamespaceManager. (This answer should really just be a comment on that answer, but I don't quite have enough Rep Points to comment.)
just use //id instead of /id. It works fine in my code
Roisgoen's answer worked for me, but to make it more general, you can use a RegEx:
//Substitute "My_RootNode" for whatever your root node is
string strRegex = #"<My_RootNode(?<xmlns>\s+xmlns([\s]|[^>])*)>";
var myMatch = new Regex(strRegex, RegexOptions.None).Match(myXmlDoc.InnerXml);
if (myMatch.Success)
{
var grp = myMatch.Groups["xmlns"];
if (grp.Success)
{
myXmlDoc.InnerXml = myXmlDoc.InnerXml.Replace(grp.Value, "");
}
}
I fully admit that this is not a best-practice answer, but but it's an easy fix and sometimes that's all we need.

Categories

Resources