1

If we have this xml file and I want to use the attribute Author as a variable to get the title

<bookstore>
 <book author="Tommy">
   <title>Emma</title>
 </book>
</bookstore>

I know that I must write this

string au = "Tommy";
string query = String.Format("//bookstore/book[@author={0}]/title", au);

If we also have this example and I want to get the title

<bk:bookstore xmlns:bk="http://www.example.com/">
  <book author="Tommy">
    <title>Emma</title>
  </book>
</bk:bookstore>

I know that I must write this

XmlNamespaceManager nsmgr = new XmlNamespaceManager(xml.NameTable);
nsmgr.AddNamespace("bk", "http://www.test.com/");
XmlNodeList elements0 = xml.SelectNodes("//bk:bookstore/book/title", nsmgr);

But I don't know what to do if I have the second example and I also want to use the attribute Author as a variable. I have tried this

string au = "Tommy";
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xml.NameTable);
nsmgr.AddNamespace("bk", "http://www.test.com/");
XmlNodeList elements0 = xml.SelectNodes("//bk:bookstore/book[@author={0}]/title", au, nsmgr); 

or this

XmlNodeList elements0 = xml.SelectNodes("//bk:bookstore/book[@author={0}]/title", nsmgr, au); 

or this

XmlNodeList elements0 = xml.SelectNodes("//bk:bookstore/book[@author={1}]/title", nsmgr, au); 

but it doesn't work. Could anybody help me?

1
  • 1
    Your XML has the namespace http://www.example.com/, but you're using http://www.test.com/ in your namespace manager. Commented Jan 2, 2015 at 17:12

1 Answer 1

1

You are mixing things up.

First build a path. Then use it. The namespace manager only matters for the second step.

string au = "Tommy";
string path = String.Format("//bk:bookstore/book[@author = '{0}']/title", au);

XmlNamespaceManager nsmgr = new XmlNamespaceManager(xml.NameTable);
nsmgr.AddNamespace("bk", "http://www.test.com/");

XmlNodeList elements0 = xml.SelectNodes(path, nsmgr); 

Note the single quotes in the path.


Also note that this will break when there is a single quote in the input string (au). This can be a source of random run-time errors in the best case and attack vector for XPath injection in the worst case. Which means you must handle that situation.

Your options to work around this issue are:

  1. Explicitly forbid single quotes in the input and additionally use au.Replace("'", "") when building the path. This is probably not suitable for person names.
  2. Build a more complex path that allows single quotes. This isn't trivial since XPath does not have an escaping mechanism for strings.
  3. Use a more advanced way of defining an XPath query.

For option 2, assuming the author "O'Reilly", the path would need to look like this:

//bk:bookstore/book[@author = concat('O', "'", 'Reilly')]/title

because the expression concat('O', "'", 'Reilly') produces the string "O'Reilly" in the XPath engine. Using concat() in this manner is the only way to embed delimiting quotes into XPath strings.

This function produces such an expression:

public static string XPathEscape(string input)
{
    if (String.IsNullOrEmpty(input)) return "''";

    if (input.Contains("'"))
    {
        string[] parts = input.Split("'".ToCharArray());
        return String.Format("concat('{0}')", String.Join(@"', ""'"", '", parts));
    }
    else
    {
        return String.Format("'{0}'", input);
    }
}

use it as follows:

string au = "O'Reilly";
string path = String.Format("//bk:bookstore/book[@author = {0}]/title", XPathEscape(au));

Note that there are no single quotes in the path this time.

Sign up to request clarification or add additional context in comments.

Comments

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.