Xml-SelectNodes with default-namespace via XmlNamespaceManager not working as expected
// This will fail with dotnet 3.5sp1. Why?
//!!!!
Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count);
This is a FAQ. In XPath any unprefixed name is assumed to be in "no namespace". In order to select elements that belong to a namespace, in any XPath expression their names must be prefixed with a prefix that is associated with this namespace. The AddNamespace()
method serves exactly this purpose. It creates a binding between a specific namespace and a specific prefix. Then, if this prefix is used in an XPath expression, the element prefixed by it can be selected.
It is written in the XPath W3C spec: "A QName in the node test is expanded into an expanded-name using the namespace declarations from the expression context. This is the same way expansion is done for element type names in start and end-tags except that the default namespace declared with xmlns is not used: if the QName does not have a prefix, then the namespace URI is null".
See this at: w3.org/TR/xpath/#node-tests .
So, any unprefixed name is considered to be in "no namespace". In the provided XML document there are no b
elements in "no namespace" and this is why the XPath expression //b
selects no nodes at all.
Use:
XmlNamespaceManager nsmanager = new XmlNamespaceManager(doc.NameTable);
nsmanager.AddNamespace("x", "urn:test.Schema");
and later:
Assert.AreEqual(2, doc.SelectNodes("//x:b", nsmanager).Count);
Remember: The whole purpose of registering the namespace is to be able to use the prefix (in this case x
) in any XPath expression.
How to select Nodes in XML correctly?
The problem is that you have a namespace in your XML: <server xmlns="exampleServer">
. When using XPath, it's assumed that elements have no namespace if you don't add a prefix in your path. See the answer to a related question.
In your situation you need to use an XmlNamespaceManager with your XPath and specify the namespace of the elements you are searching for:
var nsMgr = new XmlNamespaceManager(doc.NameTable);
nsMgr.AddNamespace("es", "exampleServer");
var properties = root.SelectNodes("//es:system-properties/es:property", nsMgr);
See this fiddle for a test run.
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);
PowerShell XML select node with namespace
As @AnsgarWiechers stated, every node has to be prefixed by its namespace because there is no inheritance.
MWE
$StandaloneXML = "test.xml"
# Load XML content
$NewStandaloneXML = New-Object -TypeName "System.XML.XMLDocument"
$NewStandaloneXML.Load($StandaloneXML)
# Get namespace
$Namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $NewStandaloneXML.NameTable
$Namespace.AddNamespace("jboss", $NewStandaloneXML.DocumentElement.NamespaceURI)
$NewStandaloneXML.SelectNodes("jboss:server/jboss:interfaces/jboss:interface", $Namespace)
To make things easier, I have built a small function to automatically prefix each nodes in the XPath
provided.
function Select-XMLNode {
[CmdletBinding()]
Param (
[Parameter (
Position = 1,
Mandatory = $true,
HelpMessage = "XML content"
)]
[ValidateNotNullOrEmpty()]
[System.XML.XMLDocument]
$XML,
[Parameter (
Position = 2,
Mandatory = $true,
HelpMessage = "XPath corresponding to the node"
)]
[ValidateNotNullOrEmpty()]
[String]
$XPath,
[Parameter (
Position = 3,
Mandatory = $false,
HelpMessage = "Namespace"
)]
[ValidateNotNullOrEmpty()]
[String]
$Namespace = $XML.DocumentElement.NamespaceURI
)
Begin {
# Variables
$Delimiter = "/"
$Alias = "x"
$SpecialCharacters = [RegEx]::New('^[/.@]*')
if ($XPath -match $SpecialCharacters) {
$Prefix = $Matches[0]
$XPath = $XPath -replace $SpecialCharacters, ''
}
}
Process {
# Get namespace
$NamespaceManager = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $XML.NameTable
$NamespaceManager.AddNamespace($Alias, $Namespace)
# Split XPath to identify nodes
$Nodes = $XPath.Split($Delimiter)
$PrefixedNodes = New-Object -TypeName "System.Collections.ArrayList"
# Prefix nodes with namespace (alias)
foreach($Node in $Nodes) {
if ($Node) {
[Void]$PrefixedNodes.Add("${Alias}:${Node}")
}
}
# Join prefixed-nodes to create new XPath with namespace
$XPathWithNamespace = $PrefixedNodes -join $Delimiter
# Check XPath prefix
if ($Prefix) {
$XPathWithNamespace = $Prefix + "" + $XPathWithNamespace
}
# Select and return nodes
$SelectedNodes = $XML.SelectNodes($XPathWithNamespace, $NamespaceManager)
return $SelectedNodes
}
}
Powershell: [xml].SelectNodes doesn't return expected results?
Your XML has default namespace declared at the document element level. Note that descendant elements inherit ancestor default namespace implicitly, unless otherwise specified. That means, CodeAnalysisRules
element you're targeting to select is in the default namespace, that's why you need to use registered prefix Any
to select the element, as mentioned by @PetSerAl in comment :
$nodes = $xml.SelectNodes("//Any:CodeAnalysisRules",$ns)
C# namespace manager doesn't work parsing a XML with namespace
There are three issues to correct:
You misdefine the namespace associated with
gesmes
.Change
ns.AddNamespace("gesmes", "http://www.ecb.int/vocabulary/2002-08-01/eurofxref");
to
ns.AddNamespace("gesmes", "http://www.gesmes.org/xml/2002-08-01");
Your XPath doesn't take into account that
Cube
and its descendants are in the default namespace.Create a prefix for the default namespace:
ns.AddNamespace("eu", "http://www.ecb.int/vocabulary/2002-08-01/eurofxref");
Update your XPath with the namespace prefix from #2:
/gesmes:Envelope/eu:Cube/eu:Cube/eu:Cube
^^^ ^^^ ^^^(
Cube
cubed? )
After fixing the above issues, your code should work as expected.
Update a specific node in XML using XmlDocument
You only supplied a piece of the xml without the namespaces. With Xml linq you can get the element without the namespaces. See code below :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = @"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement ListStart = doc.Descendants().Where(x => x.Name.LocalName == "ListStart").FirstOrDefault();
ListStart.SetValue("14.11.2020");
}
}
}
How to Select XML Nodes with XML Namespaces with C#
I am not sure what is special about your XML structure.
I would write the code little differently
string xmlNamespace = String.Empty;
XmlNamespaceManager nsmgr;
XmlNodeList nodeInfo = FABRequestXML.GetElementsByTagName("RootNodeName");
xmlNamespace = Convert.ToString(nodeInfo[0].Attributes["xmlns"].Value);
nsmgr = new XmlNamespaceManager(MyXml.NameTable);
nsmgr.AddNamespace("AB", xmlNamespace);
XmlNode myNode = MyXml.DocumentElement.SelectSingleNode("AB:NodeName", nsmgr);
Hope that helps
Related Topics
How to Call a Vbscript File in a C# Application
How to Generate .Net 4.0 Classes from Xsd
How to Get Command Line Arguments of Other Processes from .Net/C#
How to Kill a Process Using Vb.Net or C#
Bind to Selecteditems from Datagrid or Listbox in Mvvm
Which Cryptographic Hash Function Should I Choose
What Happens If a Finally Block Throws an Exception
Unique Random String Generation
How to Find and Replace Text in a File
How to Detect the Language of a String
An Attempt Was Made to Access a Socket in a Way Forbidden by Its Access Permissions. Why
Best Way in .Net to Manage Queue of Tasks on a Separate (Single) Thread
.Net: Determine the Type of "This" Class in Its Static Method
Why Cannot C# Generics Derive from One of the Generic Type Parameters Like They Can in C++ Templates