Using Xpath with default namespace in C #

I have an XML document with a default namespace. I use XPathNavigator to select a set of nodes using Xpath as follows:

XmlElement myXML = ...; XPathNavigator navigator = myXML.CreateNavigator(); XPathNodeIterator result = navigator.Select("/outerelement/innerelement"); 

I am not getting any results: I assume that this is because I do not specify a namespace. How to include a namespace in my choice?

+57
c # xml namespaces xpath xpathnavigator
Feb 25 '09 at 12:34
source share
13 answers

At first you do not need a navigator; SelectNodes / SelectSingleNode should be enough.

However, you may need a namespace manager, for example:

 XmlElement el = ...; //TODO XmlNamespaceManager nsmgr = new XmlNamespaceManager( el.OwnerDocument.NameTable); nsmgr.AddNamespace("x", el.OwnerDocument.DocumentElement.NamespaceURI); var nodes = el.SelectNodes(@"/x:outerelement/x:innerelement", nsmgr); 
+76
Feb 25 '09 at 12:40
source share

You might want to try the XPath Visualizer tool to help you.

XPathVisualizer is free, easy to use.

alt text

IMPORTANT: If you use Windows 7/8 and do not see the menu items "File", "Edit" and "Help", press the ALT key.

+48
Dec 12 '09 at 17:17
source share

For those who are looking for a quick hack solution, especially when you know XML and don’t have to worry about namespaces and all that, you can work around this nasty little function by simply reading the file in a line and replacing the offending attribute:

 XmlDocument doc = new XmlDocument(); string fileData = File.ReadAllText(fileName); fileData = fileData.Replace(" xmlns=\"", " whocares=\""); using (StringReader sr = new StringReader(fileData)) { doc.Load(sr); } XmlNodeList nodeList = doc.SelectNodes("project/property"); 

I find it easier than all the other non-sense that require a prefix for the default namespace when I am dealing with a single file. Hope this helps.

+24
Apr 18 '14 at 17:40
source share

When using XPath in .NET (via the navigator or SelectNodes / SelectSingleNode) in XML with namespaces, you need to:

  • provide your own XmlNamespaceManager

  • and explicitly prefix all the elements in the XPath expression that are in the namespace.

The latter (paraphrased from the MS source linked below): since XPath 1.0 ignores the default namespace specifications (xmlns = "some_namespace"). Therefore, when you use the name of an element without a prefix, it takes an empty namespace.

Why does the XPath .NET implementation ignore the namespace with the String.Empty prefix in the XmlNamespaceManager and always use the null namespace.

See XmlNamespaceManager and UndefinedXsltContext do not handle the default namespace for more information.

I find this “feature” very inconvenient because you cannot create an old XPath namespace by simply adding a default namespace declaration, but how it works.

+19
May 26 '10 at 8:15
source share

You can use the XPath instruction without using the XmlNamespaceManager, for example:

 ... navigator.Select("//*[ local-name() = 'innerelement' and namespace-uri() = '' ]") ... 

This is an easy way to select an element in XML with a default namespace definition.

The goal is to use:

 namespace-uri() = '' 

which will find an element with a default namespace without using prefixes.

+6
Mar 14 '12 at 13:19
source share

If namespaces are different for outerelement and innerelement

 XmlNamespaceManager manager = new XmlNamespaceManager(myXmlDocument.NameTable); manager.AddNamespace("o", "namespaceforOuterElement"); manager.AddNamespace("i", "namespaceforInnerElement"); string xpath = @"/o:outerelement/i:innerelement" // For single node value selection XPathExpression xPathExpression = navigator.Compile(xpath ); string reportID = myXmlDocument.SelectSingleNode(xPathExpression.Expression, manager).InnerText; // For multiple node selection XmlNodeList myNodeList= myXmlDocument.SelectNodes(xpath, manager); 
+5
Feb 25 '09 at 12:55
source share

I ran into a similar problem with the default empty namespace. In this XML example, I have a combination of elements with namespace prefixes and one element (DataBlock) without:

 <src:SRCExample xmlns="urn:some:stuff:here" xmlns:src="www.test.com/src" xmlns:a="www.test.com/a" xmlns:b="www.test.com/b"> <DataBlock> <a:DocID> <a:IdID>7</a:IdID> </a:DocID> <b:Supplimental> <b:Data1>Value</b:Data1> <b:Data2/> <b:Extra1> <b:More1>Value</b:More1> </b:Extra1> </b:Supplimental> </DataBlock> </src:SRCExample> 

I tried using XPath, which worked in XPath Visualizer, but did not work in my code:

  XmlDocument doc = new XmlDocument(); doc.Load( textBox1.Text ); XPathNavigator nav = doc.DocumentElement.CreateNavigator(); XmlNamespaceManager nsman = new XmlNamespaceManager( nav.NameTable ); foreach ( KeyValuePair<string, string> nskvp in nav.GetNamespacesInScope( XmlNamespaceScope.All ) ) { nsman.AddNamespace( nskvp.Key, nskvp.Value ); } XPathNodeIterator nodes; XPathExpression failingexpr = XPathExpression.Compile( "/src:SRCExample/DataBlock/a:DocID/a:IdID" ); failingexpr.SetContext( nsman ); nodes = nav.Select( failingexpr ); while ( nodes.MoveNext() ) { string testvalue = nodes.Current.Value; } 

I narrowed it down to the XPath “DataBlock” element, but couldn't get it working, except for a simple substitution of the DataBlock element:

  XPathExpression workingexpr = XPathExpression.Compile( "/src:SRCExample/*/a:DocID/a:IdID" ); failingexpr.SetContext( nsman ); nodes = nav.Select( failingexpr ); while ( nodes.MoveNext() ) { string testvalue = nodes.Current.Value; } 

After scrambling and googling multiple times (which landed on me here), I decided to tackle the default namespace directly in my XmlNamespaceManager, changing it to:

  foreach ( KeyValuePair<string, string> nskvp in nav.GetNamespacesInScope( XmlNamespaceScope.All ) ) { nsman.AddNamespace( nskvp.Key, nskvp.Value ); if ( nskvp.Key == "" ) { nsman.AddNamespace( "default", nskvp.Value ); } } 

So now "default" and "point to the same namespace. Once I have done that, XPath" / src: SRCExample / default: DataBlock / a: DocID / a: IDID "returned my results as I wanted Hope this helps clarify the issue for others.

+5
Sep 27 '11 at 19:50
source share

My answer extends Brandon's previous answer. I used his example to create an extension method as follows:

 static public class XmlDocumentExt { static public XmlNamespaceManager GetPopulatedNamespaceMgr(this System.Xml.XmlDocument xd) { XmlNamespaceManager nmsp = new XmlNamespaceManager(xd.NameTable); XPathNavigator nav = xd.DocumentElement.CreateNavigator(); foreach (KeyValuePair<string,string> kvp in nav.GetNamespacesInScope(XmlNamespaceScope.All)) { string sKey = kvp.Key; if (sKey == "") { sKey = "default"; } nmsp.AddNamespace(sKey, kvp.Value); } return nmsp; } } 

Then in my XML syntax code I just add one line:

 XmlDocument xdCandidate = new XmlDocument(); xdCandidate.Load(sCandidateFile); XmlNamespaceManager nmsp = xdCandidate.GetPopulatedNamespaceMgr(); // 1-line addition XmlElement xeScoreData = (XmlElement)xdCandidate.SelectSingleNode("default:ScoreData", nmsp); 

I really like this method because it is completely dynamic in terms of loading namespaces from the source XML file and does not completely ignore the concept of XML namespaces, so it can be used with XML, which requires several namespaces for deconfliction.

+5
May 4 '16 at 4:21
source share

In my case, adding a prefix was impractical. Too much xml or xpath was detected at runtime. In the end, I extended the methods to XmlNode. This has not been optimized for performance, and it probably does not handle all cases, but it has been working for me so far.

  public static class XmlExtenders { public static XmlNode SelectFirstNode(this XmlNode node, string xPath) { const string prefix = "pfx"; XmlNamespaceManager nsmgr = GetNsmgr(node, prefix); string prefixedPath = GetPrefixedPath(xPath, prefix); return node.SelectSingleNode(prefixedPath, nsmgr); } public static XmlNodeList SelectAllNodes(this XmlNode node, string xPath) { const string prefix = "pfx"; XmlNamespaceManager nsmgr = GetNsmgr(node, prefix); string prefixedPath = GetPrefixedPath(xPath, prefix); return node.SelectNodes(prefixedPath, nsmgr); } public static XmlNamespaceManager GetNsmgr(XmlNode node, string prefix) { string namespaceUri; XmlNameTable nameTable; if (node is XmlDocument) { nameTable = ((XmlDocument) node).NameTable; namespaceUri = ((XmlDocument) node).DocumentElement.NamespaceURI; } else { nameTable = node.OwnerDocument.NameTable; namespaceUri = node.NamespaceURI; } XmlNamespaceManager nsmgr = new XmlNamespaceManager(nameTable); nsmgr.AddNamespace(prefix, namespaceUri); return nsmgr; } public static string GetPrefixedPath(string xPath, string prefix) { char[] validLeadCharacters = "@/".ToCharArray(); char[] quoteChars = "\'\"".ToCharArray(); List<string> pathParts = xPath.Split("/".ToCharArray()).ToList(); string result = string.Join("/", pathParts.Select( x => (string.IsNullOrEmpty(x) || x.IndexOfAny(validLeadCharacters) == 0 || (x.IndexOf(':') > 0 && (x.IndexOfAny(quoteChars) < 0 || x.IndexOfAny(quoteChars) > x.IndexOf(':')))) ? x : prefix + ":" + x).ToArray()); return result; } } 

Then in your code just use something like

  XmlDocument document = new XmlDocument(); document.Load(pathToFile); XmlNode node = document.SelectFirstNode("/rootTag/subTag"); 

Hope this helps

+3
Jan 13 '10 at 6:30
source share

I used the hacked but useful approach described by SpikeDog above. It worked very well until I threw an xpath expression at it that used pipes to combine multiple paths.

So, I rewrote it with regular expressions and thought I was sharing:

 public string HackXPath(string xpath_, string prefix_) { return System.Text.RegularExpressions.Regex.Replace(xpath_, @"(^(?![A-Za-z0-9\-\.]+::)|[A-Za-z0-9\-\.]+::|[@|/|\[])(?'Expression'[A-Za-z][A-Za-z0-9\-\.]*)", x => { int expressionIndex = x.Groups["Expression"].Index - x.Index; string before = x.Value.Substring(0, expressionIndex); string after = x.Value.Substring(expressionIndex, x.Value.Length - expressionIndex); return String.Format("{0}{1}:{2}", before, prefix_, after); }); } 
+1
Oct 20 '10 at 16:11
source share

Or, if someone should use an XPathDocument like me:

 XPathDocument xdoc = new XPathDocument(file); XPathNavigator nav = xdoc.CreateNavigator(); XmlNamespaceManager nsmgr = new XmlNamespaceManager(nav.NameTable); nsmgr.AddNamespace("y", "http://schemas.microsoft.com/developer/msbuild/2003"); XPathNodeIterator nodeIter = nav.Select("//y:PropertyGroup", nsmgr); 
+1
Jul 03 2018-12-12T00:
source share

In this case, this is probably the namespace resolution that is causing the problem, but it is also possible that your XPath expression itself is not correct. You can evaluate it first.

Here is the code using XPathNavigator.

 //xNav is the created XPathNavigator. XmlNamespaceManager mgr = New XmlNamespaceManager(xNav.NameTable); mgr.AddNamespace("prefix", "http://tempuri.org/"); XPathNodeIterator result = xNav.Select("/prefix:outerelement/prefix:innerelement", mgr); 
0
Feb 25 '09 at 12:48
source share

1] If you have an XML file without a prefix in the namespace:

 <bookstore xmlns="http://www.contoso.com/books"></bookstore> 

you have this workaround:

 XmlTextReader reader = new XmlTextReader(@"C:\Temp\books.xml"); // ignore the namespace as there is a single default namespace: reader.Namespaces = false; XPathDocument document = new XPathDocument(reader); XPathNavigator navigator = document.CreateNavigator(); XPathNodeIterator nodes = navigator.Select("//book"); 

2] If you have an XML file with a prefix in the namespace:

 <bookstore xmlns:ns="http://www.contoso.com/books"></bookstore> 

Use this:

 XmlTextReader reader = new XmlTextReader(@"C:\Temp\books.xml"); XPathDocument document = new XPathDocument(reader); XPathNavigator navigator = document.CreateNavigator(); XPathNodeIterator nodes = navigator.Select("//book"); 

Of course, you can use space name management if necessary:

 XmlTextReader reader = new XmlTextReader(@"C:\Temp\books.xml"); XPathDocument document = new XPathDocument(reader); XPathNavigator navigator = document.CreateNavigator(); XmlNamespaceManager nsmgr = new XmlNamespaceManager(reader.NameTable); nsmgr.AddNamespace("ns", "http://www.contoso.com/book"); XPathNodeIterator nodes = navigator.Select("//book", nsmgr); 

I think this is the easiest way to get the code to work in most cases.

I hope this helps solve this Microsoft problem ...

0
Jan 10 '19 at 12:39
source share



All Articles