C# parsing Xml SelectingNode - c#

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().

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

Creating XmlNode with specified spread sheet type

I'm little bit new in xml manipulation. I want to create a XmlNode.
I've already tried to OwnerDocument.CreateElement method and also tried OwnerDocument.CreateNode method, but I can't create the following XmlNode:
<Data ss:Type="String"/>
Can you help me with this problem? I already tried everything which I found, but nothing.
XmlDocument has been superseded in the .NET framework by the newer XDocument API, which plays nicer with Linq and in general is a modern library to use for XML manipulation.
Here is an example how you can use that API to insert an element into an existing XML document, with an attribute that has a namespace prefix which is previously declared.
XDocument ownerDocument = XDocument.Parse("<OwnerDocument></OwnerDocument>");
XNamespace ssNameSpace = "http://whatever/somenamespace";
// add namespace declaration to the root, set ss as the namespace prefix
// you only need to do this if the document doesn't already have the namespace declaration
ownerDocument.Root.Add(new XAttribute(XNamespace.Xmlns + "ss", ssNameSpace.NamespaceName));
// add our new data element to the root, and add the type attribute prefixed with the ss namespace
ownerDocument.Root.Add(new XElement("Data", new XAttribute(ssNameSpace + "Type", "String")));
That will produce the following XML:
<OwnerDocument xmlns:ss="http://whatever/somenamespace">
<Data ss:Type="String" />
</OwnerDocument>
If you are really tied to using XmlDocument, you can acheive the same there as follows:
XmlDocument ownerDocument = new XmlDocument();
ownerDocument.LoadXml("<OwnerDocument></OwnerDocument>");
// add namespace declaration to the root, set ss as the namespace prefix
var nsDeclarationAttribute = ownerDocument.CreateAttribute("xmlns:ss");
nsDeclarationAttribute.Value = "http://whatever/somenamespace";
ownerDocument.DocumentElement.Attributes.Append(nsDeclarationAttribute);
// add data element, and add a type attribute to that
var dataElement = ownerDocument.CreateElement("Data");
var typeAttribute = ownerDocument.CreateAttribute("Type", "http://whatever/somenamespace");
typeAttribute.Value = "String";
dataElement.Attributes.Append(typeAttribute);
// append to main document
ownerDocument.DocumentElement.AppendChild(dataElement);

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

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"

Can i create a XmlNamespaceManager object from the xml file i am about to read?

I have some c# code running on sharepoint that i use to check inside the xml of an infopath document to see if i should checking the document or discard the document.
The code is working fine for a couple of different form templates i have created but is failing on my latested one.
I have discovered that the XmlNamespaceManager i am creating contains the wrong diffinition for the "MY" namepsace.
I'll try to explain
I have this code to decalre my XmlNamespaceManager
NameTable nt = new NameTable();
NamespaceManager = new XmlNamespaceManager(nt);
// Add prefix/namespace pairs to the XmlNamespaceManager.
NamespaceManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
NamespaceManager.AddNamespace("xhtml", "http://www.w3.org/1999/xhtml");
NamespaceManager.AddNamespace("dfs", "http://schemas.microsoft.com/office/infopath/2003/dataFormSolution");
NamespaceManager.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-07-14T13:45:59");
NamespaceManager.AddNamespace("xd", "http://schemas.microsoft.com/office/infopath/2003");`
I can then use the following line of code to search for the xml i am after
XPathNavigator nav = xml.CreateNavigator().SelectSingleNode("//my:HasSaved", NamespaceManager);
nav.Value then gets be the data i want.
This all works fine on a couple of my form templates. I ahve a new form template and have discovered that i need to use thi line instead
NamespaceManager.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-11-30T17:39:37");
The date is different.
My problem is that i cannot add this twice as only 1 set of forms will work.
So my question is. Is there a way i can generate the NamespaceManager object from the XML file as this is all contained in the header?
I have not been able to find a simple way round this.
I found a way of doing this. Instead of adding the "my" namespace it can be pulled from the XmlDocument object. This might just be a bit of luck that it works this way but i'm happy with it.
NamespaceManager.AddNamespace("my", formXml.DocumentElement.NamespaceURI
formXML is an XmlDocument created from the infopath XML
One option would be to try to load the xml node using the first namespace, if that doesn't give any results, call PushScope(), override the first namespace definition, select, etc...
var doc = new XmlDocument();
doc.LoadXml(#"<?xml version=""1.0""?>
<root xmlns:my=""http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-11-30T17:39:37"" value=""1"">
<my:item>test</my:item>
</root>");
var nameTable = new NameTable();
var namespaceManager = new XmlNamespaceManager(nameTable);
namespaceManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
namespaceManager.AddNamespace("xhtml", "http://www.w3.org/1999/xhtml");
namespaceManager.AddNamespace("dfs", "http://schemas.microsoft.com/office/infopath/2003/dataFormSolution");
namespaceManager.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-07-14T13:45:59");
namespaceManager.AddNamespace("xd", "http://schemas.microsoft.com/office/infopath/2003");
// n will be null since the namespace url doesn't match
var n = doc.SelectSingleNode("descendant::my:item", namespaceManager);
namespaceManager.PushScope();
namespaceManager.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-11-30T17:39:37");
// will work
n = doc.SelectSingleNode("descendant::my:item", namespaceManager);
namespaceManager.PopScope();
Another option is to parse the attributes in the header and look for any contained namespaces
foreach(XmlAttribute attribute in doc.DocumentElement.Attributes)
{
var url = namespaceManager.LookupNamespace(attribute.LocalName);
if(url != null && url != attribute.Value)
{
namespaceManager.RemoveNamespace(attribute.LocalName, url);
namespaceManager.AddNamespace(attribute.LocalName, attribute.Value);
}
}
You don't need to do anything like this. Just use "my2" or something for the second namespace. You then need the new namespace prefix for any nodes that use the new namespace, and use the old "my" namespace prefix for all the nodes that still use the old namespace.

Categories

Resources