Apostrophe (') in XPath query

Viewed 44283

I use the following XPATH Query to list the object under a site. ListObject[@Title='SomeValue']. SomeValue is dynamic. This query works as long as SomeValue does not have an apostrophe ('). Tried using escape sequence also. Didn't work.

What am I doing wrong?

11 Answers

I really like Robert's answer, but I feel like the code could be a little denser.

using System.Linq;

namespace Humig.Csp.Common
{
    public static class XpathHelpers
    {
        public static string XpathLiteralEncode(string literalValue)
        {
            return string.IsNullOrEmpty(literalValue)
                ? "''"
                : !literalValue.Contains("\"")
                ? $"\"{literalValue}\""
                : !literalValue.Contains("'")
                ? $"'{literalValue}'"
                : $"concat({string.Join(",'\"',", literalValue.Split('"').Select(k => $"\"{k}\""))})";
        }
    }
}

I have also created a unit test with all the test cases:

using HtmlAgilityPack;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Humig.Csp.Common.Tests
{
    [TestClass()]
    public class XpathHelpersTests
    {

        [DataRow("foo")]   // no quotes
        [DataRow("\"foo")]   // double quotes only
        [DataRow("'foo")]   // single quotes only
        [DataRow("'foo\"bar")]   // both; double quotes in mid-string
        [DataRow("'foo\"bar\"baz")]   // multiple double quotes in mid-string
        [DataRow("'foo\"")]   // string ends with double quotes
        [DataRow("'foo\"\"")]   // string ends with run of double quotes
        [DataRow("\"'foo")]   // string begins with double quotes
        [DataRow("\"\"'foo")]   // string begins with run of double quotes
        [DataRow("'foo\"\"bar")]   // run of double quotes in mid-string
        [TestMethod()]
        public void XpathLiteralEncodeTest(string attrValue)
        {
            var doc = new HtmlDocument();
            var hnode = doc.CreateElement("html");
            var body = doc.CreateElement("body");
            var div = doc.CreateElement("div");
            div.Attributes.Add("data-test", attrValue);
            doc.DocumentNode.AppendChild(hnode);
            hnode.AppendChild(body);
            body.AppendChild(div);
            var literalOut = XpathHelpers.XpathLiteralEncode(attrValue);
            string xpath = $"/html/body/div[@data-test = {literalOut}]";
            var result = doc.DocumentNode.SelectSingleNode(xpath);
            Assert.AreEqual(div, result, $"did not find a match for {attrValue}");

        }
    }
}
Related