SelectSingleNode return null - even with namespace - c#

I know this question has been asked in a similar fashion before, but I can't seem to get this working.
I have some xml:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<Research xmlns="http://www.rixml.org/2005/3/RIXML" xmlns:xalan="http://xml.apache.org/xalan" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" createDateTime="2011-03-29T15:41:48Z" language="eng" researchID="MusiJvs3008">
<Product productID="MusiJvs3008">
<StatusInfo currentStatusIndicator="Yes" statusDateTime="2011-03-29T15:41:48Z" statusType="Published" />
<Source>
<Organization type="SellSideFirm" primaryIndicator="Yes">
<OrganizationID idType="Reuters">9999</OrganizationID>
And I'm trying to read values using xpath:
XPathDocument xmldoc = new XPathDocument(xmlFile);
XPathNavigator nav = xmldoc.CreateNavigator();
XmlNamespaceManager nsMgr = new XmlNamespaceManager(nav.NameTable);
nsMgr.AddNamespace(string.Empty, "http://www.rixml.org/2005/3/RIXML");
XPathNavigator result = nav.SelectSingleNode("/Research", nsMgr); // <-- Returns null!
But even a simple select of the root node returns null! I am sure I have something wrong with my namespace. Can someone please help?
Ideally I want simple lines that will let me select values from the xml file, i.e.
String a = xmlDoc.SelectSingleNode(#"/Research/Product/Content/Title").Value;
BTW, I have no (direct) control over the XML file content.

I don't believe you can use an empty namespace alias and have it used automatically by the XPath expression. As soon as you use an actual alias, it should work though. This test is fine, for example:
using System;
using System.Xml;
using System.Xml.XPath;
class Test
{
static void Main()
{
string xmlFile = "test.xml";
XPathDocument xmldoc = new XPathDocument(xmlFile);
XPathNavigator nav = xmldoc.CreateNavigator();
XmlNamespaceManager nsMgr = new XmlNamespaceManager(nav.NameTable);
nsMgr.AddNamespace("x", "http://www.rixml.org/2005/3/RIXML");
XPathNavigator result = nav.SelectSingleNode("/x:Research", nsMgr);
Console.WriteLine(result);
}
}
Do you have to use XPath and XPathDocument, by the way? I tend to find that LINQ to XML is a much more pleasant API, particularly when it comes to namespaces. If you're using .NET 3.5 and you have no particular requirement to use XPath, I'd suggest you check it out.

Make the following changes
nsMgr.AddNamespace("x", "http://www.rixml.org/2005/3/RIXML");
XPathNavigator result = nav.SelectSingleNode("/x:Research", nsMgr);

i could post, and answer my own question, for the native equivalent of this code. Instead i'll just add it as an answer to the end.
When using the native IXMLDOMDocument (version 6) object:
//Give the default namespace as alias of "x"
document.setProperty("SelectionNamespaces","xmlns:x='http://www.rixml.org/2005/3/RIXML'");
//Query for the nodes we want
document.selectSingleNode("/x:Research");
Bonus Question: Why, oh why, does no Xml Document object model query the default namespace when no namespace is specified... sigh

Related

Trying to read particular Xml value using C#

I'm new with C#. please help me.
data.xml
<?xml version="1.0" standalone="yes"?>
<DataSet1 xmlns="http://tempuri.org/DataSet1.xsd">
<Language>
<Id>001</Id>
<English>"Welcome to India"</English>
<German>"Willkommen in Indien"</German>
</Language>
</DataSet1>
How to read value of Id using C# Xml? I'm new with C#. please help me.
My effort:
XmlDocument doc = new XmlDocument();
XmlReader reader = XmlReader.Create(#"D:\data.xml");
while(reader.Read())
{
reader.MoveToContent();
if (reader.IsStartElement("DataSet1"))
{
reader.ReadToDescendant("Language");
string str2 = reader.Name.ToString();
MessageBox.Show(str2);
}
}
I'd strongly advise you to use LINQ to XML, which is a much simpler API than XmlDocument, and has excellent namespace support. Here's an example to get the Id value with LINQ to XML:
using System;
using System.Xml.Linq;
public class Program
{
public static void Main()
{
XDocument doc = XDocument.Load("test.xml");
XNamespace ns = "http://tempuri.org/DataSet1.xsd";
string id = doc.Root // From the root...
.Element(ns + "Language") // select the Language direct child...
.Element(ns + "Id") // and the Id child of that...
.Value; // and then take the text value
Console.WriteLine(id);
}
}
Obviously if your XML doesn't just have a single Language element you'll need to pick the right one, etc. You should read the LINQ to XML documentation for more information.
Use XmlDocument, if you Need further help leave a comment :)
XmlDocument xdoc = new XmlDocument("YourXmlString");
XmlNodeList xNodes = xdoc.GetElementsByTagName("Id");
string Wished = xNodes[0].InnerText;
Little bit of explanation:
I recommend XmlDocument a lot because I really like to work with it myself, and besides that it has some pretty neat features to explore. Besides that, it is easy to get or to iterate through nodes

C# Using XPath with XmlDocument - Can't select nodes in a namespace (returning null)

I'm trying to do something which ought to be quite simple but I'm having terrible trouble. I have tried code from multiple similar questions in StackOverflow but to no avail.
I'm trying to get various pieces of information from an ABN lookup with the Australian government. Here is anonymised return XML value:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<ABRSearchByABNResponse xmlns="http://abr.business.gov.au/ABRXMLSearch/">
<ABRPayloadSearchResults>
<request>
<identifierSearchRequest>
<authenticationGUID>00000000-0000-0000-0000-000000000000</authenticationGUID>
<identifierType>ABN</identifierType>
<identifierValue>00 000 000 000</identifierValue>
<history>N</history>
</identifierSearchRequest>
</request>
<response>
<usageStatement>The Registrar of the ABR monitors the quality of the information available on this website and updates the information regularly. However, neither the Registrar of the ABR nor the Commonwealth guarantee that the information available through this service (including search results) is accurate, up to date, complete or accept any liability arising from the use of or reliance upon this site.</usageStatement>
<dateRegisterLastUpdated>2017-01-01</dateRegisterLastUpdated>
<dateTimeRetrieved>2017-01-01T00:00:00.2016832+10:00</dateTimeRetrieved>
<businessEntity>
<recordLastUpdatedDate>2017-01-01</recordLastUpdatedDate>
<ABN>
<identifierValue>00000000000</identifierValue>
<isCurrentIndicator>Y</isCurrentIndicator>
<replacedFrom>0001-01-01</replacedFrom>
</ABN>
<entityStatus>
<entityStatusCode>Active</entityStatusCode>
<effectiveFrom>2017-01-01</effectiveFrom>
<effectiveTo>0001-01-01</effectiveTo>
</entityStatus>
<ASICNumber>000000000</ASICNumber>
<entityType>
<entityTypeCode>PRV</entityTypeCode>
<entityDescription>Australian Private Company</entityDescription>
</entityType>
<goodsAndServicesTax>
<effectiveFrom>2017-01-01</effectiveFrom>
<effectiveTo>0001-01-01</effectiveTo>
</goodsAndServicesTax>
<mainName>
<organisationName>COMPANY LTD</organisationName>
<effectiveFrom>2017-01-01</effectiveFrom>
</mainName>
<mainBusinessPhysicalAddress>
<stateCode>NSW</stateCode>
<postcode>0000</postcode>
<effectiveFrom>2017-01-01</effectiveFrom>
<effectiveTo>0001-01-01</effectiveTo>
</mainBusinessPhysicalAddress>
</businessEntity>
</response>
</ABRPayloadSearchResults>
</ABRSearchByABNResponse>
</soap:Body>
</soap:Envelope>
so I want to get for example the whole response using xpath="//response" then use various xpath statement within that node to get the <organisationName> ("//mainName/organisationName") and other values.
It should be simple right? Those xpath statements appear to work when testing in Notepad++but I use this code in Visual Studio:
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(ipxml);
XmlNode xnode = xdoc.SelectSingleNode("//response");
XmlNodeList xlist = xdoc.SelectNodes("//mainName/organisationName");
xlist = xdoc.GetElementsByTagName("mainName");
But it always returns null, whatever I put in the xpath I get a null return for the node and 0 count for the list whether I'm selecting something with child nodes, a value or not.
I can get the nodes using GetElementsByTagName() as in the example which returns the correct node, but I wanted to do it 'properly' selecting the proper field using xpath.
I also tried using XElement and Linq but still no luck. Is there something weird about the XML?
I'm sure it must something simple but I've been struggling for ages.
You aren't dealing with the namespaces present in the document. Specifically, the high level element:
<ABRSearchByABNResponse xmlns="http://abr.business.gov.au/ABRXMLSearch/">
places ABRSearchByABNResponse, and all its child elements (unless overridden by another xmlns) into the namespace http://abr.business.gov.au/ABRXMLSearch/. In order to navigate to these nodes (without hacks like GetElementsByTagName or using local-name()), you'll need to register the namespaces with an XmlNamespaceManager, like so. The xmlns aliases don't necessarily need to match those used in the original document, but it's a good convention to do so:
XmlDocument
var xdoc = new XmlDocument();
var ns = new XmlNamespaceManager(xdoc.NameTable);
ns.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
ns.AddNamespace("abr", "http://abr.business.gov.au/ABRXMLSearch/");
xdoc.LoadXml(ipxml);
// NB need to use the overload accepting a namespace
var xresponse = xdoc.SelectSingleNode("//abr:response", ns);
var xlist = xdoc.SelectNodes("//abr:mainName/abr:organisationName", ns);
XDocument
More recently, the powers of LINQ can be harnessed with XDocument, which makes working with namespaces much easier (Descendants finds child nodes at any depth)
var xdoc = XDocument.Parse(ipxml);
XNamespace soap = "http://schemas.xmlsoap.org/soap/envelope/";
XNamespace abr = "http://abr.business.gov.au/ABRXMLSearch/";
var xresponse = xdoc.Descendants(abr + "response");
var xlist = xdoc.Descendants(abr + "organisationName");
XDocument + XPath
You can also resort to using XPath in Linq to Xml, especially for more complicated expressions:
var xdoc = XDocument.Parse(ipxml);
var ns = new XmlNamespaceManager(new NameTable());
ns.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
ns.AddNamespace("abr", "http://abr.business.gov.au/ABRXMLSearch/");
var xresponse = xdoc.XPathSelectElement("//abr:response", ns);
var xlist = xdoc.XPathSelectElement("//abr:mainName/abr:organisationName", ns);
You need to call SelectSingleNode and SelectNodes on the DocumentElement. You are calling them on the document itself.
For example:
XmlNode xnode = xdoc.DocumentElement.SelectSingleNode("//response");

Select XML Nodes by Attribute Value

I have a oracle query to generate below xml result and I like to check only if there is any attribute value inside SUBQUERY and COL_LIST_ITEM nodes not others. If nodes has attribute value than write "it has attribute value" else "no attribute value". I am not sure if my code is correct! Is there any way to do that?
<VIEW xmlns="http://xmlns.oracle.com/ku" version="1.0">
<SCHEMA value1="USER1">USER2</SCHEMA>
<NAME value1="VIEW_TBL_A">VIEW_TBL_B</NAME>
<COL_LIST>
<COL_LIST_ITEM>
</COL_LIST_ITEM>
<COL_LIST_ITEM src="2">
<NAME>FIELD_A2</NAME>
</COL_LIST_ITEM>
</COL_LIST>
<SUBQUERY value1="SELECT FIELD_A1
FROM TBL_A
WHERE FIELD_A1 = 111">SELECT FIELD_A1, FIELD_A2
FROM TBL_A
WHERE FIELD_A1 = 111</SUBQUERY>
</VIEW>
Here is my Code
using (OracleConnection conn1 = new OracleConnection(oradb1))
{
conn1.Open();
using (OracleCommand crtCommand = new OracleCommand("SELECT dbms_metadata.get_sxml('VIEW','VIEW_TBL_A') FROM dual;", conn1))
{
XmlDocument xml = new XmlDocument();
xml.LoadXml(crtCommand.ExecuteScalar().ToString());
}
}
how about an XDocument
XDocument doc = XDocument.Parse(crtCommand.ExecuteScalar().ToString());
XNamespace ns = "http://xmlns.oracle.com/ku";
if (doc.Descendants(ns + "COL_LIST_ITEM").Any(c => c.Attributes().Any()))
Console.WriteLine("COL_LIST has value");
and of course add your other nodes in to check
You should use LINQ to XML to query your XML document:
XDocument document = XDocument.Load(xmlFile);
IEnumerable<XElement> elements =
from element in document.Root.Elements("SUBQUERY")
where element.HasAttributes
select element;
Refer to the article here for more details.
I also ran into a similar problem, while working on a Universal Windows App, and was also very frustrated when VS didn't specify clearly what the problem was. Although after hours of surfing the web, I found the solution. It was simple, but i don't know why VS didn't specify it. Anyways, here it is :-
Add another reference to your project :-
using System.Linq; apart from using System.Xml.Linq;
using System.Linq;
using System.Xml.Linq;
The errors will go & the project will build.
For more help reference this article :-
problem with using refrence to System.Xml.Linq

C#: Read XML Attribute

Using C#2.0 and VIsualStudio2005
I'm trying to access the "DisplayName" inside "MonitorResponseRecord"
from an XML file like the one Below:
<Magellan xsi:schemaLocation="http://tempuri.org/XMLSchema.xsd ..\Schema\Configuration.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/XMLSchema.xsd">
<SchemaVersion>1.0</SchemaVersion>
<MonitorScope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="CleanStationChemicalManifoldFeed5" xmlns="http://tempuri.org/XMLSchema.xsd">
<PersonalSafety>
<MonitorResponseRecord Enabled="true" DisplayName="ChemicalManifoldFeed5ControllerFault">
<ExpressionMonitor>
<Expression>(ChemicalManifold.Feed5.DispenseValve = Open) and ((ChemicalManifold.Feed5.ViolatedRegion = HighProcess) or (ChemicalManifold.Feed5.ViolatedRegion = LowProcess) or (ChemicalManifold.Feed5.ViolatedRegion = Minimum))</Expression>
<TestInterval>0.1</TestInterval>
<MinimumTimeBetweenResponses>5</MinimumTimeBetweenResponses>
</ExpressionMonitor>
<Response>
<PostAlarm>
<AlarmName>ChemicalManifold_Feed5_ControllerFault</AlarmName>
<Parameter1 />
<Parameter2>Alarm Region = {ChemicalManifold.Feed5.ViolatedRegion}</Parameter2>
<Parameter3>{RecipeName}-{StepName}</Parameter3>
<Parameter4>FlowSetpoint = {ChemicalManifold.Feed5.Setpoint}-LPM, ActualFlow = {ChemicalManifold.Feed5.FlowMeter}-LPM</Parameter4>
</PostAlarm>
<ResponseEvent>
<TargetResource />
<Event>PA</Event>
<Reason>ChemicalSupply</Reason>
</ResponseEvent>
</Response>
</MonitorResponseRecord>
</PersonalSafety>
<PersonalSafety>
<MonitorResponseRecord Enabled="true" DisplayName = "PressureValveFailure">
...
...
...and soon
My current C# attempt is as follows, BUT it always hangs up when I try to XmlDocument.Load("");
XmlDocument doc = new XmlDocument();
doc.Load("../UMC0009.Configuration.Root.xml");
string attrVal = doc.SelectSingleNode("MonitorResponseRecord/#DisplayName").Value;
BUUUUT won't work :/ any help out there?
UPDATE:
the exception I recieve is as follows, and occures at the doc.Load("...") line:
{"Namespace Manager or XsltContext needed. This query has a prefix, variable, or user-defined function."} System.Exception {System.Xml.XPath.XPathException}
Your XPath query will be relative to the document root:
doc.SelectSingleNode("MonitorResponseRecord/#DisplayName")
To make it search anywhere in the doc prefix it with double slash:
doc.SelectSingleNode("//MonitorResponseRecord/#DisplayName")
If that still doesn't work I would try the above example after stripping out all those namespace declarations on the two root nodes.
Otherwise, with the namespace declarations you may find you need to define XML namespace mappings and use prefixes in your XPath like:
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("x", "http://tempuri.org/XMLSchema.xsd");
doc.SelectSingleNode("//x:MonitorResponseRecord/#DisplayName")
What about:
XmlDocument doc = new XmlDocument();
doc.Load("UMC0009.Configuration.Root.xml");
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("ns", "http://tempuri.org/XMLSchema.xsd");
string attrVal = doc.SelectSingleNode("//ns:MonitorResponseRecord/#DisplayName", nsmgr).Value;
Using a namespace manager, specify your namespace URI and use it in your XPath.
It works for me.

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