Using Xpath With Default Namespace in C#

Using Xpath With Default Namespace in C#

First - you don't need a navigator; SelectNodes / SelectSingleNode should suffice.

You may, however, 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);

C# XPATH with namespaces

This is how it is working with the xml you posted. In case the element can be found noting will be done to the saved xml.

var doc = new XmlDocument();
doc.Load("D:/Chrome/Quartz.NET-2.4.1/src/Quartz.Examples/quartz_jobs.xml");

var namespaceManager = new XmlNamespaceManager(doc.NameTable);
namespaceManager.AddNamespace("x", "http://quartznet.sourceforge.net/JobSchedulingData");

XmlNode selectedNode = doc.SelectSingleNode("x:job-scheduling-data/x:schedule/x:trigger/x:cron/x:cron-expression", namespaceManager);
if (selectedNode != null)
{
selectedNode.InnerXml = "0";
doc.Save("D:/Chrome/Quartz.NET-2.4.1/src/Quartz.Examples/quartz_jobs.xml");
}

How do I use XPath with a default namespace with no prefix?

The configuration element is in the unnamed namespace, and the MyNode is bound to the lcmp namespace without a namespace prefix.

This XPATH statement will allow you to address the MyNode element without having declared the lcmp namespace or use a namespace prefix in your XPATH:

/configuration/*[namespace-uri()='lcmp' and local-name()='MyNode']

It matches any element that is a child of configuration and then uses a predicate filer with namespace-uri() and local-name() functions to restrict it to the MyNode element.

If you don't know which namespace-uri's will be used for the elements, then you can make the XPATH more generic and just match on the local-name():

/configuration/*[local-name()='MyNode']

However, you run the risk of matching different elements in different vocabularies(bound to different namespace-uri's) that happen to use the same name.

XPATHS and Default Namespaces

I tried something similar to what palehorse proposed and could not get it to work. Since I was getting data from a published service I couldn't change the xml. I ended up using XmlDocument and XmlNamespaceManager like so:

XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlWithBogusNamespace);
XmlNamespaceManager nSpace = new XmlNamespaceManager(doc.NameTable);
nSpace.AddNamespace("myNs", "http://theirUri");

XmlNodeList nodes = doc.SelectNodes("//myNs:NodesIWant",nSpace);
//etc

Use XPath with XML namespace

Default namespace has different nature. The element where the default namespace declared and all of it's descendant without different namespace declaration considered in the same default namespace. Therefore, you need to use the prefix for all descendats as well. This XPath worked fine for me :

XElement doc = XElement.Parse(xml);
XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable());
ns.AddNamespace("prefix", "http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration");
XElement settingDiagConnectionString = doc.XPathSelectElement("//prefix:Role/prefix:ConfigurationSettings/prefix:Setting[1]", ns);
settingDiagConnectionString.SetAttributeValue("value", "hello, world!");

UPDATE :

Responding to your update. That's because you're using XElement. In this case, doc it self already represents <ServiceConfiguration> element so you don't need to include "ServiceConfiguration" in the XPath :

/prefix:Role/prefix:ConfigurationSettings/prefix:Setting[1]

..or use XDocument instead of XElement if you want to make it work using the same XPath as for XmlDocument.

Using Xpath With Default Namespace in C# for Canonicalisation

I've found a partial answer to my problem.

When a new namespace is added to the manager it appears that the default namespace can't be an empty string.
This is what I ended up with:

//Instantiate an XmlNamespaceManager object. 
System.Xml.XmlNamespaceManager xmlnsManager = new System.Xml.XmlNamespaceManager(xDoc.NameTable);

//Add the namespaces used to the XmlNamespaceManager.
xmlnsManager.AddNamespace("x", "http://www.myApps.co.uk/");

I then needed to modify the XPath to reflect the namespace identifier like this:

// Create a list of nodes to have the Canonical treatment
//Execute the XPath query using the SelectNodes method of the XmlDocument.
//Supply the XmlNamespaceManager as the nsmgr parameter.
//The matching nodes will be returned as an XmlNodeList.
XmlNodeList nodeList = xDoc.SelectNodes("/x:ApplicationsBatch/x:Applications|/x:ApplicationsBatch/x:Applications//*", xmlnsManager);

The nodes are now selected and ready for transformation... although that returns the correct structure of XML but all the values have been removed but that is a problem for another question.

How do I select nodes that use a default namespace?

You need to assign a namespace prefix for the default namespace that the document is using, and then use that in your XPath:

XmlDocument doc= new XmlDocument();
doc.Load(filepath);

XmlNamespaceManager m = new XmlNamespaceManager(doc.NameTable);
m.AddNamespace("myns", "url1");

XmlNodeList l = doc.SelectNodes("/myns:a/myns:b/myns:c", m);

You can replace the prefix "myns" with essentially anything (alphanumeric without spaces), as long as it's consistent between line 4 and the XPath, and that it's correctly assigned to the "url1" namespace in line 4.

Select Nodes with XPath when the XML document contains namespaces

You have two namespaces here. First is

http://schemas.datacontract.org/2004/07/Company.Product.Components.Model

Root element (ComponentSettings), RemoteTracer and everything below it belong to this namespace. Second namespace is

http://schemas.datacontract.org/2004/07/Company.Configuration

Created, LastLoaded and Saved belong to it.

To get the node you need, you have to prefix all elements in your xpath query with their respective namespace prefixes. Mapping of those prefixes to actual namespaces you can do like this:

var componentConfigXmlDocument = new XmlDocument();            
componentConfigXmlDocument.LoadXml(File.ReadAllText(@"G:\tmp\xml.txt"));
var ns = new XmlNamespaceManager(componentConfigXmlDocument.NameTable);
ns.AddNamespace("model", "http://schemas.datacontract.org/2004/07/Company.Product.Components.Model");
ns.AddNamespace("config", "http://schemas.datacontract.org/2004/07/Company.Configuration");

And then query like this:

var remoteTracers = componentConfigXmlDocument.SelectNodes("//model:RemoteTracer/model:TraceListener/model:Url", ns);

Why does NamespaceManager not use DefaultNamespace when using no prefix in XPath

@JLRishe's answer is correct for accessing nodes in the default namespace (ie. always mapping a prefix to the default namespace in the XmlNamespaceManager).

Reading the entire context of the link from your quote (MSDN XmlNamespaceManager.AddNamespace) it is stated that the default "empty" prefix is not used in XPath expressions.

prefix
Type: System.String

The prefix to associate with the namespace being added. Use String.Empty to add a default namespace.>

Note If the XmlNamespaceManager will be used for resolving namespaces in an XML Path Language (XPath) expression, a prefix must be specified. If an XPath expression does not include a prefix, it is assumed that the namespace Uniform Resource Identifier (URI) is the empty namespace. For more information about XPath expressions and the XmlNamespaceManager, refer to the XmlNode.SelectNodes and XPathExpression.SetContext methods.

Xpath of Element with namespace

Well, first of all /:Report is not a valid XPath expression, if your used function creates that path then it does not output a valid XPath in case the XML document has a default namespace like xmlns="http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition". In fact given such a default namespace declaration in the input any function that creates a path needs to generate a prefix as well (and multiple for different namespace in general) and bind it to the default namespace URI. Or you need to take a different approach and generate steps of the form *[local-name() = 'Report' and namespace-uri() = 'http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition']. With XPath 1.0 that is the way I would take as the generation of prefixes and the binding of them to namespace URIs is depending on the particular XPath API you use while the other approach will work with any XPath API.



Related Topics



Leave a reply



Submit