1

I've been using this absolute XPath extension to get XPath strings of nodes inside XDocument's. The node can then later be queried by XPath using xdoc.XPathSelectElement(xpath).

However, this fails for XML documents that use namespaces, like the following:

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.svsxml.svs.com" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Header>
        <wsse:Security soapenv:mustUnderstand="1">
            <wsse:UsernameToken>
                <wsse:Username>Pizza</wsse:Username>
                <wsse:Password>Pie</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
    </soapenv:Header>
</soapenv:Envelope>

Calling GetAbsoluteXPath on the Username node gives:

/Envelope/Header[1]/Security[1]/UsernameToken[1]/Username[1]

However, querying the XDocument with XPathSelectElement and the above XPath yields null, because no namespace is specified.

According to answers I looked up, the solution is to add a NamespaceManager and manually edit the path. However, my paths are dynamically-generated (I don't know the namespace or structure of the XML document in advance), so hand-tweaking the strings is not an option.

My question is:

  • Is there a way to query using an XPath in a way which totally ignores namespaces?

An answer to a similarly-named question was to query by local name instead of XPath. However, this fails when multiple nodes have the name LocalName (which often occurs in the XML I'm parsing), since searching by name only throws away the specificity of the XPath entirely.

For clarification: I don't know what the XML document looks like in advance, and the XPath and namespaces are determined at run-time, not compile-time. So manually adding strings that only work for this particular example won't work in general.

Community
  • 1
  • 1
Peter Richter
  • 755
  • 1
  • 6
  • 15

2 Answers2

0

The code below is xml linq and will work with one node. With multiple nodes you have to add addional search info to get the exact node(s) you are looking for.

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 envelope = (XElement)doc.FirstNode;
            XNamespace wsse =  envelope.GetNamespaceOfPrefix("wsse");
            string username = envelope.Descendants(wsse + "Username").FirstOrDefault().Value;

        }
    }
}
​
jdweng
  • 33,250
  • 2
  • 15
  • 20
  • Thanks, but this won't work because I don't know the namespace (or even what the XML looks like) in advance. – Peter Richter Dec 26 '15 at 02:13
  • To search xml you need to know what you are looking for. You response doesn't make any sense. – jdweng Dec 26 '15 at 02:15
  • I'm dynamically querying an unknown XML document, so the XPath and namespaces will only be known at runtime, but not compile-time. – Peter Richter Dec 26 '15 at 02:19
  • You can replace any hard coded string (in double quotes) with a variable so a runtime it can be dynamic. – jdweng Dec 26 '15 at 09:01
0

I had the same requirement and found a solution like this:

XDocument doc = ...
var nsmngr = new XmlNamespaceManager(doc.CreateReader().NameTable);

foreach (var attribute in doc.Descendants().Attributes().Where(a => a.IsNamespaceDeclaration))
{
    nsmngr.AddNamespace(attribute.Name.LocalName, attribute.Value);
}

// then use the namespacemanager when querying the document
var someXPath = "/Envelope/Header[1]/Security[1]/UsernameToken[1]/Username[1]";
doc.XPathEvaluate(someXPath, nsmngr)
jbutcher
  • 23
  • 1
  • 7