We help IT Professionals succeed at work.

Please show me the XPath syntax using SelectSingleNode()

I am having trouble using SelectSingleNode() to find the node from the XmlDocument.

I can get the root node thusly. But I want to find the "reminderBody" node.


                var xmlDoc = new XmlDocument();
                xmlDoc.Load(PublishedContentPath + xmlFile);
                XmlNode rootNode = xmlDoc.SelectSingleNode("//content-item");

Please use a test tool such as:
http://www.yetanotherchris.me/home/2010/6/7/online-xpath-tester.html

to prove that your XPath syntax works then provide me the C# synta, if you could.

Thanks. I am stuck.

Cheers!

<?xml version="1.0" encoding="utf-8"?>
<content-item>
<createDate >
11/30/2011</createDate>
<lastModified >
11/30/2011</lastModified>
<displayTitle >
repayment</displayTitle>
<author >
me</author>
<analytics >
<![CDATA[	<meta content="6560" name="unknown" />
]]>
</analytics>
<messages>
<message name="reminderHeader">
<![CDATA[<h1>Hey, consider yourself reminded</h1>]]>
</message>
<message name="reminderBody">
<![CDATA[Standard reminder text]]>
</message>
</messages>
</content-item>

Open in new window

Comment
Watch Question

Most Valuable Expert 2011
Top Expert 2015

Commented:
Think of XPath like you would navigating to a folder on your filesystem: each child (i.e. typically indented node) is a new "subfolder". Granted, it does get a bit tricky when you introduce attributes, but the syntax to work with atts is not terribly difficult. For your example, it should be:

/content-item/message[@name="reminderHeader"]

Open in new window

Most Valuable Expert 2011
Top Expert 2015

Commented:
P.S.

The above selects the <message> node that has a name equal to "reminderHeader". The brackets are like an if condition. If you wanted to select the attribute itself (i.e. "name:), then you would add that to the "folder" path:

/content-item/message[@name="reminderHeader"]/@name

Open in new window


I left the condition in there to ensure that the "reminderHeader" element is chosen.

I realize now that I mistyped, and it should be "reminderBody" instead. Please change accordingly.
curiouswebsterSoftware Engineer

Author

Commented:
I just want the node. How do I do that?
Most Valuable Expert 2011
Top Expert 2015

Commented:
What do you mean by "node"? Are you referring to the CDATA content? Can you post what you expect to acquire?
curiouswebsterSoftware Engineer

Author

Commented:
Maybe I am just confused. I wanted the XmlNode which is a child node to the root node "content-item"

<messages>
<message name="reminderBody">
<![CDATA[Standard reminder text]]>
</message>
</messages>

Having the Node would allow me to extract the Value, but also see the InnerHtml, and other properties as needed. That node (with attribute name="reminderBody") should have no child nodes.

Do I understand this right?
curiouswebsterSoftware Engineer

Author

Commented:
The XML is valid. But, I just tested your Xpath in the test tool that I posted and it produced no output:

I posted the XML and tried these:

/content-item/message[@name="reminderHeader"]
/content-item/message[@name="reminderHeader"]/@name
//content-item/message[@name="reminderHeader"]
//content-item/message[@name="reminderHeader"]/@name
Most Valuable Expert 2011
Top Expert 2015
Commented:
I see that I missed one level of indentation, so my previous submission is flawed, but not far from correct. The corrected XPath should be:

/content-item/messages[message/@name="reminderBody"]

Open in new window


That will give you the <messages> node; however, you will get all children as well. This means "requestHeader" and "requestBody" will both come.

My previous suggestion (corrected to include "messages") would return you just the <message> node that was the "requestBody". Here's an example of both XPaths in action:

Code
using System;
using System.Xml;

namespace _27480345
{
    class Program
    {
        static void Main(string[] args)
        {
            XmlDocument xdoc = new XmlDocument();

            xdoc.Load("source.xml");

            XmlNode messages = xdoc.SelectSingleNode("/content-item/messages[message/@name='reminderBody']");

            Console.WriteLine("Messages Node:\n==============================================\n");
            Console.WriteLine(messages.OuterXml);
            Console.WriteLine();

            XmlNode requestBody = xdoc.SelectSingleNode("/content-item/messages/message[@name='reminderBody']");

            Console.WriteLine("Reminder Body Node:\n==============================================\n");
            Console.WriteLine(requestBody.OuterXml);
            Console.WriteLine();
            Console.ReadKey();
        }
    }
}

Open in new window



untitled.PNG
curiouswebsterSoftware Engineer

Author

Commented:
But the attached sample works! I am confused.

 XPath Success
curiouswebsterSoftware Engineer

Author

Commented:
Sweet!  Thank you very much!
curiouswebsterSoftware Engineer

Author

Commented:
/content-item/messages[message/@name='reminderBody']

works on the test tool as well!
Most Valuable Expert 2011
Top Expert 2015

Commented:
To get output similar to what you see in the screenshot you provided, you simply need to inspect the Value property of the node returned by SelectSingleNode. The Value property returns all the text data found inside of the node--including all children. The example you originally provided is a bit different because you have CDATA. The element returned by the XPath:

/content-item/messages/message[@name='reminderBody']

Open in new window


is an Element type. The Value property returns null for such nodes types. For your simple example, though, you can navigate to the first child of that node, which should give you the CDATA node. CDATA nodes return there internal value when the Value property is called on them. Modifying my previous example:

using System;
using System.Xml;

namespace _27480345
{
    class Program
    {
        static void Main(string[] args)
        {
            XmlDocument xdoc = new XmlDocument();

            xdoc.Load("source.xml");

            XmlNode requestBody = xdoc.SelectSingleNode("/content-item/messages/message[@name='reminderBody']");

            Console.WriteLine("Reminder Body Node:\n==============================================\n");
            Console.WriteLine(requestBody.FirstChild.Value);
            Console.WriteLine();
            Console.ReadKey();
        }
    }
}

Open in new window



untitled.PNG
curiouswebsterSoftware Engineer

Author

Commented:
Thanks again!