Xml-Selectnodes with Default-Namespace via Xmlnamespacemanager Not Working as Expected

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:

  1. 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");
  2. 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");
  3. 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



Leave a reply



Submit