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

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"

Related

How to read a node in a serialized XML file?

I'm trying to read a node in a serialized XML file. Here is the the first part of the XML file (I'm using a screen cap because pasting ended up with weird formatting):
And this is the code I'm using to read the XML and the error it's throwing:
I'm trying to read the <ScenarioDescription> node.
As per request, here's the entire XML file. Unfortunately it's just a complete mess. Here is a link to the XML file.
You should You would need to specify the namespace. In this particular case, the default namespace is used to declare the http://schemas.datacontract.org/2004/07/ModelLib namespace.
var xml = new XmlDocument();
xml.LoadXml(str);
XmlNamespaceManager ns = new XmlNamespaceManager(xml.NameTable);
ns.AddNamespace("x", xml.DocumentElement.NamespaceURI);
var root = xml.DocumentElement;
var test = root.SelectSingleNode("//x:ScenarioDescription",ns);
var scenarioText = test.InnerText;
You can use the following code to access the ScenarioDescription node and its InnerText
var document = new XmlDocument();
document.Load(s);
var root = document.DocumentElement;
var node = root["ScenarioDescription"];
var text = node?.InnerText;
SelectSingleNode accepts the xpath expression, you simply can use XmlElement indexer instead. Otherwise you'll need to create a XmlNamespaceManager instance and add your root namespace to it
You can supply the XmlNamespaceManager object as a parameter to the
SelectNodes or SelectSingleNode method of the XmlDocument class to
execute XPath query expressions that reference namespace-qualified
element and attribute name

Set XML string using node.innerXML - use of prefix causes error

I am creating a huge xml from DB and most of it's part is hard-coded(pre-defined values),so I decided to create string using StringBuilder and assigning it to node's InnerXML but prefix used for one of the attribute is not allowing me to set the string stating "Prefix not declared".
XMLNode addr = DocumentElement.createElement("element","address",null);
addr.InnerXML = "<test><s1>ttt</s1><s2 xsi:type="test">yyy</s2></test>";
prefix xsi is causing the error here. Is there any workaround for this.
xsi seems to be a namespace. You will need to either declare that namespace or not use it. Using it without declaring it is invalid.
Had some similar problem, the only way I found to fix this problem, is to add the needed namespace to your InnserXml:
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml("<Document xmlns=\"www.abc.com\" xmlns:xsi=\"w3.org/2001/XMLSchema-instance\"></Document>");
XmlNode t = xdoc.CreateElement("element", "t", null);
t.InnerXml = "<originalText xmlns:xsi=\"w3.org/2001/XMLSchema-instance\"><reference xsi:value=\"testVal\"/></originalText>";
xdoc.FirstChild.AppendChild(t);
The workaround is:
Firstly simply create XMLDocument and assign plain innerXML without namespace and prefixes as
XmlDocument ccda_xDoc = new XmlDocument();
ccda_xDoc.LoadXml("<ClinicalDocument></ClinicalDocument>");
XmlElement root = ccda_xDoc.DocumentElement;
root.InnerXml = #" My XML String without Prefix for any namespace";
Then Search for the node where you want to apply prefix to and add attribute with prefix:
XmlNode xsiValue = root.SelectSingleNode("encounter/code[#code='99201']");
XmlAttribute xsitype = structuredBody.CreateAttribute("sdtc", "valueSet", "urn:hl7-org:sdtc");
xsitype.Value = "2.16.840.1.113883.3.464.1003.101.12.1047";
xsiValue.Attributes.Prepend(xsitype);
Finally Apply namespaces to root node as :
ccda_xDoc.DocumentElement.SetAttribute("xmlns", "urn:hl7-org:v3");
ccda_xDoc.DocumentElement.SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
ccda_xDoc.DocumentElement.SetAttribute("xmlns:voc", "urn:hl7-org:v3/voc");
ccda_xDoc.DocumentElement.SetAttribute("xmlns:sdtc", "urn:hl7-org:sdtc");

C# parsing Xml SelectingNode

I'm having troubles using Xml on my C# program.
the xml http://pastebin.com/Ufxaght6 (from sandbox)
I'm trying to get any info on the XML, I succeed using recursive loop on nodes, but I want to use something more efficient.
I'm trying this :
XmlDocument document = new XmlDocument();
document.LoadXml(response);
XmlNode node = document.SelectSingleNode("/getnewsalesresult/request/user");
if (node != null)
Logger.WriteLine(node.InnerText);
else
Logger.WriteLine("fail");
This gives always a null.
I think the problem comes from the 'getnewsalesresult' (Wildcard maybe ?).
From the XML :
<getnewsalesresult xmlns="http://www.sandbox.priceminister.com/res/schema/getnewsales" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sandbox.priceminister.com/res/schema/getnewsales http://www.sandbox.priceminister.com/res/schema/getnewsales/getnewsales.2014-02-11.xsd">
Any idea ?
Thanks in advance.
Your XML has default namespace (xmlns="..."). Default namespace has a different nature. The element where the default namespace declared and all of it's descendant without prefix and without different namespace declaration considered in the same default namespace.
The easiest way to get element in namespace is just to ignore the namespace (as also suggested in comment to this question) :
string xpath = "/*[local-name()='getnewsalesresult']/*[local-name()='request']/*[local-name()='user']";
XmlNode node = document.SelectSingleNode(xpath);
A more proper way is to register a prefix that mapped to the default namespace URI, then use that prefix in your XPath :
XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable());
ns.AddNamespace("d", "http://www.sandbox.priceminister.com/res/schema/getnewsales");
XmlNode node =
document.SelectSingleNode("/d:getnewsalesresult/d:request/d:user", ns);
don't miss to pass XmlNamespaceManager object as 2nd parameter of SelectSingleNode().

XPathSelectElements returns null

Load function is already defined in xmlData class
public class XmlData
{
public void Load(XElement xDoc)
{
var id = xDoc.XPathSelectElements("//ID");
var listIds = xDoc.XPathSelectElements("/Lists//List/ListIDS/ListIDS");
}
}
I'm just calling the Load function from my end.
XmlData aXmlData = new XmlData();
string input, stringXML = "";
TextReader aTextReader = new StreamReader("D:\\test.xml");
while ((input = aTextReader.ReadLine()) != null)
{
stringXML += input;
}
XElement Content = XElement.Parse(stringXML);
aXmlData.Load(Content);
in load function,im getting both id and and listIds as null.
My test.xml contains
<SEARCH>
<ID>11242</ID>
<Lists>
<List CURRENT="true" AGGREGATEDCHANGED="false">
<ListIDS>
<ListID>100567</ListID>
<ListID>100564</ListID>
<ListID>100025</ListID>
<ListID>2</ListID>
<ListID>1</ListID>
</ListIDS>
</List>
</Lists>
</SEARCH>
EDIT: Your sample XML doesn't have an id element in the namespace with the nss alias. It would be <nss:id> in that case, or there'd be a default namespace set up. I've assumed for this answer that in reality the element you're looking for is in the namespace.
Your query is trying to find an element called id at the root level. To find all id elements, you need:
var tempId = xDoc.XPathSelectElements("//nss:id", ns);
... although personally I'd use:
XDocument doc = XDocument.Parse(...);
XNamespace nss = "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner";
// Or use FirstOrDefault(), or whatever...
XElement idElement = doc.Descendants(nss + "id").Single();
(I prefer using the query methods on LINQ to XML types instead of XPath... I find it easier to avoid silly syntax errors etc.)
Your sample code is also unclear as you're using xDoc which hasn't been declared... it helps to write complete examples, ideally including everything required to compile and run as a console app.
I am looking at the question 3 hours after it was submitted and 41 minutes after it was (last) edited.
There are no namespaces defined in the provided XML document.
var listIds = xDoc.XPathSelectElements("/Lists//List/ListIDS/ListIDS");
This XPath expression obviously doesn't select any node from the provided XML document, because the XML document doesn't have a top element named Lists (the name of the actual top element is SEARCH)
var id = xDoc.XPathSelectElements("//ID");
in load function,im getting both id and and listIds as null.
This statement is false, because //ID selects the only element named ID in the provided XML document, thus the value of the C# variable id is non-null. Probably you didn't test thoroughly after editing the XML document.
Most probably the original ID element belonged to some namespace. But now it is in "no namespace" and the XPath expression above does select it.
string xmldocument = "<response xmlns:nss=\"http://schemas.microsoft.com/SQLServer/reporting/reportdesigner\"><action>test</action><id>1</id></response>";
XElement Content = XElement.Parse(xmldocument);
XPathNavigator navigator = Content.CreateNavigator();
XmlNamespaceManager ns = new XmlNamespaceManager(navigator.NameTable);
ns.AddNamespace("nss", "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner");
var tempId = navigator.SelectSingleNode("/id");
The reason for the null value or system returned value is due to the following
var id = xDoc.XPathSelectElements("//ID");
XpathSElectElements is System.xml.linq.XElment which is linq queried date. It cannot be directly outputed as such.
To Get individual first match element
use XPathSelectElement("//ID");
You can check the number of occurrences using XPathSelectElements as
var count=xDoc.XPathSelectElements("//ID").count();
you can also query the linq statement as order by using specific conditions
Inorder to get node value from a list u can use this
foreach (XmlNode xNode in xDoc.SelectNodes("//ListIDS/ListID"))
{
Console.WriteLine(xNode.InnerText);
}
For Second list you havnt got the value since, the XPath for list items is not correct

C# Xml Value always getting null

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.

Categories

Resources