Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apostrophe (') in XPath query

Tags:

java

c#

xml

xpath

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?

like image 312
Prabhu Avatar asked Aug 27 '09 15:08

Prabhu


People also ask

How do you put an apostrophe in XPath?

xpath("//input[contains(@text,\"WE'd\")]"));

How do you escape special characters in XPath?

There is no way to escape characters at the XPath level, so you can't have a literal XPath string containing both kinds of quote.

Can we use double quotes in XPath?

The meaning of double quotes in XML, XPath and Java The values of XML attributes are delimited by either single or double quotes. This means that you can use double quotes as content when you use single quotes as the delimter and vice versa. You can also use the entities ' and " as a content.

What is the * indicates in XPath?

Then you'd select all element nodes with an @id -attribute-value equal to 'Passwd' in the whole document. Just add //* in the XPath -- it highlights --- various page elements. This would select all element nodes in the whole document.


1 Answers

This is surprisingly difficult to do.

Take a look at the XPath Recommendation, and you'll see that it defines a literal as:

Literal ::=   '"' [^"]* '"'              | "'" [^']* "'" 

Which is to say, string literals in XPath expressions can contain apostrophes or double quotes but not both.

You can't use escaping to get around this. A literal like this:

'Some'Value' 

will match this XML text:

Some'Value 

This does mean that it's possible for there to be a piece of XML text that you can't generate an XPath literal to match, e.g.:

<elm att="&quot;&apos"/> 

But that doesn't mean it's impossible to match that text with XPath, it's just tricky. In any case where the value you're trying to match contains both single and double quotes, you can construct an expression that uses concat to produce the text that it's going to match:

elm[@att=concat('"', "'")] 

So that leads us to this, which is a lot more complicated than I'd like it to be:

/// <summary> /// Produce an XPath literal equal to the value if possible; if not, produce /// an XPath expression that will match the value. ///  /// Note that this function will produce very long XPath expressions if a value /// contains a long run of double quotes. /// </summary> /// <param name="value">The value to match.</param> /// <returns>If the value contains only single or double quotes, an XPath /// literal equal to the value.  If it contains both, an XPath expression, /// using concat(), that evaluates to the value.</returns> static string XPathLiteral(string value) {     // if the value contains only single or double quotes, construct     // an XPath literal     if (!value.Contains("\""))     {         return "\"" + value + "\"";     }     if (!value.Contains("'"))     {         return "'" + value + "'";     }      // if the value contains both single and double quotes, construct an     // expression that concatenates all non-double-quote substrings with     // the quotes, e.g.:     //     //    concat("foo", '"', "bar")     StringBuilder sb = new StringBuilder();     sb.Append("concat(");     string[] substrings = value.Split('\"');     for (int i = 0; i < substrings.Length; i++ )     {         bool needComma = (i>0);         if (substrings[i] != "")         {             if (i > 0)             {                 sb.Append(", ");             }             sb.Append("\"");             sb.Append(substrings[i]);             sb.Append("\"");             needComma = true;         }         if (i < substrings.Length - 1)         {             if (needComma)             {                 sb.Append(", ");                                 }             sb.Append("'\"'");         }      }     sb.Append(")");     return sb.ToString(); } 

And yes, I tested it with all the edge cases. That's why the logic is so stupidly complex:

    foreach (string s in new[]     {         "foo",              // no quotes         "\"foo",            // double quotes only         "'foo",             // single quotes only         "'foo\"bar",        // both; double quotes in mid-string         "'foo\"bar\"baz",   // multiple double quotes in mid-string         "'foo\"",           // string ends with double quotes         "'foo\"\"",         // string ends with run of double quotes         "\"'foo",           // string begins with double quotes         "\"\"'foo",         // string begins with run of double quotes         "'foo\"\"bar"       // run of double quotes in mid-string     })     {         Console.Write(s);         Console.Write(" = ");         Console.WriteLine(XPathLiteral(s));         XmlElement elm = d.CreateElement("test");         d.DocumentElement.AppendChild(elm);         elm.SetAttribute("value", s);          string xpath = "/root/test[@value = " + XPathLiteral(s) + "]";         if (d.SelectSingleNode(xpath) == elm)         {             Console.WriteLine("OK");         }         else         {             Console.WriteLine("Should have found a match for {0}, and didn't.", s);         }     }     Console.ReadKey(); } 
like image 148
Robert Rossney Avatar answered Oct 12 '22 21:10

Robert Rossney