12

I have an XML e.g.

    <?xml version="1.0" encoding="utf-8"?>
    <A1>
       <B2>
          <C3 id="1">
             <D7>
                <E5 id="abc" />
             </D7>
             <D4 id="1">
                <E5 id="abc" />
             </D4>
             <D4 id="2">
                <E5 id="abc" />
             </D4>
          </C3>
       </B2>
    </A1>

This is may sample code:

    var xDoc = XDocument.Load("Test.xml");
    string xPath = "//B2/C3/D4";
    //or string xPath = "//B2/C3/D4[@id='1']";

    var eleList = xDoc.XPathSelectElements(xPath).ToList();
    foreach (var xElement in eleList)
    {
        Console.WriteLine(xElement);
    }

It works perfectly, but if I add a namespace to the root node A1, this code doesn't work. Upon searching for solutions, I found this one, but it uses the Descendants() method to query the XML. From my understanding, this solution would fail if I was searching for <E5> because the same tag exists for <D7>, <D4 id="1"> and <D4 id="2">

My requirement is to search if a node exists at a particular XPath. If there is a way of doing this using Descendants, I'd be delighted to use it. If not, please guide me on how to search using the name space.

My apologies in case this is a duplicate.

3 Answers 3

17

To keep using XPath, you can use something link this:

    var xDoc = XDocument.Parse(@"<?xml version='1.0' encoding='utf-8'?>
        <A1 xmlns='urn:sample'>
            <B2>
                <C3 id='1'>
                    <D7><E5 id='abc' /></D7>
                    <D4 id='1'><E5 id='abc' /></D4>
                    <D4 id='2'><E5 id='abc' /></D4>
                </C3>
            </B2>
        </A1>");

    // Notice this
    XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
    nsmgr.AddNamespace("sample", "urn:sample");

    string xPath = "//sample:B2/sample:C3/sample:D4";

    var eleList = xDoc.XPathSelectElements(xPath, nsmgr).ToList();
    foreach (var xElement in eleList)
    {
        Console.WriteLine(xElement);
    }
Sign up to request clarification or add additional context in comments.

4 Comments

I tried this, thank you, that works. Is this the best way of doing it? I'm just asking because my function is passed the entire XPath as a string and it doesn't have the name space as shown in your xPath variable. I can add it via string replace, but just wondering if there is any other way of doing this?
The namespace prefix isn't important, just value part; so, if you rename your prefix from sample to whatever that would be ok.
may be I am missing something here, I meant that you have the prefix before each element name in the xPath variable. I was asking an alternative to this. i.e. "//sample:B2/sample:C3/sample:D4"
@Charlie, if your XML document have a namespace and you XPath expression don't, one of those need to be changed.
1

but it uses the Descendants() method to query the XML. From my understanding, this solution would fail if I was searching for because the same tag exists for , and

I'm pretty sure you're not quite understanding how that works. From the MSDN documentation:

Returns a filtered collection of the descendant elements for this document or element, in document order. Only elements that have a matching XName are included in the collection.

So in your case, just do this:

xDoc.RootNode
    .Descendants("E5")
    .Where(n => n.Parent.Name.LocalName == "B4");

3 Comments

Yes, but how would I know if it belonged to my XPath. As you see, the same E5 node occurs at multiple paths.
@Charlie, have a look at my edit. I leverage the Parent property to ensure it's part of a B4 element.
this is close, but my XPath is passed as a string and I cannot dynamically check every parent up to the root node. My XML has a deep hierarchy.
0

Try this

var xDoc = XDocument.Parse("<A1><B2><C3 id=\"1\"><D7><E5 id=\"abc\" /></D7><D4 id=\"1\"><E5 id=\"abc\" /></D4><D4 id=\"2\"><E5 id=\"abc\" /></D4></C3></B2></A1>");
        foreach (XElement item in xDoc.Element("A1").Elements("B2").Elements("C3").Elements("D4"))
        {
            Console.WriteLine(item.Element("E5").Value);//to get the value of E5
            Console.WriteLine(item.Element("E5").Attribute("id").Value);//to get the value of attribute
        }

1 Comment

I cannot do this, I get the XPath as a string, how do I implement that as the foreach loop you have? Plus the XPath can be n level deep. Is there a way to handle that?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.