XmlDocument.SelectSingleNode omit prefixes and namespaces - c#

This question is a follow up of the answered question:
XmlDocument.SelectSingleNode and prefix + xmlNamespace issue
The problem is that its possible that in the future the namespaces prefixes of the xml received will be changed without warning, so we would like to know if there is any way of using SelectSingleNode but omitting the prefix of an element.
(We know we could remove all the prefixes of the incoming xml but it would require more steps....albeit we would consider it a valid answer if code is provided...)

It doesn't matter if the prefix names change, as long as the namespace URIs do not change.
The prefix name you use in your code and the one in the XML document do not have to match, e.g.
namespaces.AddNamespace("foo", "http://exception.do29.imq.es/xsd");
XmlNode nodemsg = xmldocu.SelectSingleNode("//foo:message", namespaces);

Its possible to omit it using * in the xpath, for example:
//*[local-name()='ElementName']
Based on the question XPath select node with namespace
As an extra this tool was very useful to test different xpaths.... http://xpathvisualizer.codeplex.com/

Related

Difference between namespace in XML

I'm creating XML document using XDocument in C#.
I have a question.
Is
<Simple xmlns = "Example"></Simple>
equivalent to
<Example:Simple></Example:Simple>
?
I tried to get second solution with XNamespace and XElement in C#, but I get only first.
No.
The first example creates a Simple element in the Example namespace (note that namespaces are usually expressed as URIs)
The second example creates a Simple element in whatever namespace is associated with the Example prefix (as defined by an xmlns attribute).
These would be equivalent:
<xml xmlns="http://example.com/myNameSpace">
<Simple></Simple>
</xml>
<xml xmlns="http://example.com/myNameSpace" xmlns:Example="http://example.com/myNameSpace">
<Example:Simple></Example:Simple>
</xml>
In the first example, you have defined a default namespace which applies to any element/attribute that is not prefixed with its own namespace.
In the second example, you have not defined a namespace.
No, because xml namespaces allow for characters which aren't supported by element names, you can't prefix an element tag name with its namespace like that.
Add a namespace prefix, like so:
<alias:Simple xmlns:alias = "Example"></alias:Simple>
No, but it's equivalent to:
<Example:Simple xmlns:Example="Example"></Example:Simple>
It's a bad idea to use relative URIs as the namespace name, since this XML now has a different namespace depending on where it came from. So always give the full URI. E.g if the XML was being received from http://example.net/somePlace/someXML then the relative URI Example expands to http://example.net/somePlace/Example, so use it fully:
<Example:Simple xmlns:Example="http://example.net/somePlace/Example"></Example:Simple>
OR
<Simple xmlns="http://example.net/somePlace/Example"></Simple>
Otherwise if someone saved it in C:\Documents then on opening it again it becomes the equivalent to:
<Simple xmlns="file:///C|/Documents/Example"></Simple>
Which means that the meaning of Simple here is completely different to that when it was first downloaded.

Querying XML Without Worrying about Namespaces

I have XML with and without a prefix on elements, but no namespaces defined for any of them. When I try to load this, it gives me an error on XDocument.Load (at least, I think that's where it happens) that certain prefixes are not defined. Is there a way to tell the framework to ignore any namespace prefixes? I'm using LINQ to XML, but could use something else if available.
I can't necessarily pre-define them because I'm going to be working with a variety of documents that may or may not have a prefix defined and no definitive xmlns declaration.
Aren't prefixes supposed to represent an abbreviation for a namespace? I believe you need to clean up those prefixes that have no namespace associated with them in the first place before processing it, since it isn't valid XML. A quick regex to replace all prefixes of the form </prefix: with </: and <prefix: with < should do it.
To do this, first replace the following regex matches
</.*?: with </
and <.*?: with < (do not change the ordering).
An approach to what you want to do may be using XmlDocument:
XmlDocument d = new XmlDocument();
using (var textReader = new XmlTextReader(#"test.xml"))
{
textReader.Namespaces = false;
d.Load(textReader);
}
You will lose the power of querying the data using the syntax of LINQ to XML.
You can actually use LINQ to XML and ignore the namespace by setting for each prefix in the file the folowing line
nameSpaceManager.AddNamespace("prefixName", "urn:ignore");
where nameSpaceManager is of type XmlNamespaceManager.
But from your question i sense that this is not a reasonable solution.

Declare namespaces within XPath expression

My application needs to evaluate XPath expression against some XML data. Expression is provided by user at runtime. So, I cannot create XmlNamespaceManager to pass to XPathEvaluate because I don't know prefixes and namespaces at compile time.
Is there any possibility to specify namespaces declaration within xpath expression?
Answers to comments:
XML data has one default namespace but there can be nested elements with any namespaces. User knows namespaces of the data he works with.
User-provided xpath expression is to be evaluated against many XML documents, and every document can have its own prefixes for the same namespaces.
If the same prefix can be bound to different namespaces and prefixes aren't known in advance, then the only pure XPath way to specify such expressions is to use this form of referring to elements:
someName[namespace-uri() = 'exactNamespace']
So, a particular XPath expression would be:
/*/a[namespace-uri() = 'defaultNS']/b[namespace-uri() = 'NSB']
/c[namespace-uri() = 'defaultNS']
I don't know any way to define a namespace prefix in an XPath expression.
But you can write the XPath expression to be agnostic of namespace-prefixes by using local-name() and namespace-uri() functions where appropriate.
Or if you know the XML-namespaces in advance, you can register an arbitrary prefix for them in the XmlNamespaceManager and tell your user to use that prefix in the XPath expression. It doesn't matter if the XML document itself registers a different prefix or no prefix at all. Path resolution is based on the namespace alone, not on the prefix.
Another option would be to scan the document at runtime (use XmlReader for low resource overhead if you haven't loaded it already) and then add the used mappings in the document in the XmlNamespaceManager. I'm not sure if you can get the namespaces and prefixes from XmlDocument, but I see no direct method to do it. It's easy with XmlReader though, since it exposes NamespaceURI and Prefix members for each node.
Is there any possibility to specify namespaces declaration within xpath expression?
The answer is no - it's always done in the calling environment (which is actually more flexible).
An alternative would be to use XQuery, which does allow declaring namespaces in the query prolog.
UPDATE (2020)
In XPath 3.1 you can use the syntax /*/Q{http://my-namespace}a.
Sadly, though, if you're still using Microsoft software, then the situation hasn't changed since 2011 - you're still stuck with XPath 1.0 with all its shortcomings.

Xpath, retrieving node value

I get this return value from Sharepoint... which I have just included the first part of the xml snippet...
<Result ID=\"1,New\" xmlns=\"http://schemas.microsoft.com/sharepoint/soap/\">
<ErrorCode>0x00000000</ErrorCode><ID /><z:row ows_ID=\"9\"
It populates a XmlNode node object.
How using xPath can I get the value of ows_id ?
My code so far...
XmlNode results = list.UpdateListItems("MySharePointList", batch);
Update
So far I have this : results.FirstChild.ChildNodes[2].Attributes["ows_ID"].Value
But I am not sure how reliable it is, can anyone improve on it?
I don't know if its necessarily an improvement, but it might be more readable, though more verbose:
/*[local-name() = 'Result']/*[local-name() = 'row']/#ows_ID
There is probably more to the fragment you posted so this XPath query might need a fixup when used against the actual xml result.
The function, local-name(), lets you ignore namespaces, which can be both a boon and a curse. :)
When you start from root:
/Result/z:row/#ows_ID
also you can improve search if exists multiple Result:
/Result[#ID='1,New']/z:row/#ows_ID
<xsl:value-of select="Result/b:row/#ows_ID"/>
or
<xsl:value-of select="Result/b:row[#ows_ID = '9']"/>
Depending on what value you wanted
You probably need to make sure the z namespace prefix is declared correctly - that's implementation dependent. Here's how you do it in Java's XPath implementation.
Then to select the value of the ows_ID attribute, you need to navigate to the element itself, then use #ows_ID to get the value.
The specific xpath calls depend on what library you use (e.g. libxml xpath implementation).
But the generic xpath statement would be:
"//z:row[#ows_ID='9']"
This will select all z:row nodes with an attribute ows_ID of value 9.
You can modify this query to match all z:row nodes or only those with a specific attribute.
For details look here: W3Schools XPath syntax

Controlling the order of XML namepaces

I'm having a problem getting the "xmlns" to appear first in the root attribute list.
Im getting this:
<myroot
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.someurl.com/ns/myroot http://www.someurl.com/xml/schemas/myschema.xsd"
xmlns="http://www.someurl.com/ns/myroot">
<sometag>somecontent</sometag>
</myroot>
And i want this:
<myroot
xmlns="http://www.someurl.com/ns/myroot"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.someurl.com/ns/myroot http://www.someurl.com/xml/schemas/myschema.xsd">
<sometag>somecontent</sometag>
</myroot>
My code looks like this:
XNamespace rt = "http://www.someurl.com/ns/myroot";
XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
var submissionNode = new XElement(XmlNameSpaces.rt + "myroot");
submissionNode.Add(new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"));
submissionNode.Add(new XAttribute(xsi + "schemaLocation", #"http://www.someurl.com/ns/myroot http://www.someurl.com/xml/schemas/myschema.xsd"););
What do i need to do different to change the order?
EDIT: I understand the order is not normally relavent, but its a requirement in this case.
IIRC, the order of attributes (in xml) is unimportant... so why change it? Is it causing an actual problem?
Would XmlWriter be an option for you?
Afaik, it gives you full control of the order of attributes and namespace declarations.
Attribute ordering is NOT specified in the XML document, and shouldn't be relied upon. It may be worth looking at the spec
You'll find that if you read a XML document into a DOM, and write it out, regardless of the platform/library, you can't (and shouldn't) rely on the attribute ordering. It's a common misconception, btw!
I have a customer with this very problem. This was a real pain in the s, so I wrote a workaround to solve this.
Please note this is not a beautiful solution, and this should be not encouraged, but works.
public static class MyKludgeXmlClass
{
public static XmlDocument CreateXmlDocumentWithOrderedNamespaces()
{
var xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><MyRoot xmlns=\"http://www.example.com/schemas/1.0/VRSync\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.example.com/schemas/1.0/VRSync http://xml.example.com/vrsync.xsd\"></MyRoot>";
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.LoadXml(xml);
return doc;
}
}
With XmlDocument you can retrieve the root:
var xmlDoc = MyKludgeXmlClass.CreateXmlDocumentWithOrderedNamespaces();
XmlElement root = xmlDoc.DocumentElement;
And append children nodes using your favorite method.
Software that requires attributes to be in a specified order doesn't conform to the XML recommendation.
The first question you should be asking is not, "How can I produce XML with namespace attributes in a defined order?" Instead, it should be, "What are the other respects in which this software doesn't conform to the XML recommendation?" Because I will bet you one crisp new American dollar that if the recipient's process violates the XML recommendation in one respect, it violates it in at least one other.
Because sometimes the right answer is to say, no, don't do that...
Per W3C Namespaces in XML Recommendation, section 3 Declaring Namespaces:
[Definition: A namespace (or more precisely, a namespace binding) is
declared using a family of reserved attributes. Such an attribute's name must either be xmlns or begin xmlns:. These
attributes, like any other XML attributes, may be provided directly or
by default. ]
Therefore, the order of namespace declarations, like the order of any attributes, is insignificant.
So, no conformant XML tool or library will care about the order of namespace declarations, and neither should you.

Categories