Xpath 在有命名空间的 xml 文件中使用属性作为变量

Xpath using an attribute as a variable in an xml file where there is a namespace

如果我们有这个 xml 文件并且我想使用作者属性作为变量来获取标题

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

我知道我必须写这个

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

如果我们也有这个例子,我想获取标题

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

我知道我必须写这个

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

但是如果我有第二个例子,我不知道该怎么办,我也想使用属性Author作为变量。我试过这个

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); 

或这个

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

或这个

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

但它不起作用。 有人可以帮我吗?

你把事情搞混了。

首先构建路径。然后使用它。命名空间管理器仅对第二步重要。

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); 

注意路径中的单引号。


另请注意,当输入字符串 (au) 中存在单引号时,此 中断。在最好的情况下,这可能是随机 运行 时间错误的来源,在最坏的情况下,这可能是 XPath 注入的攻击向量。这意味着您必须处理这种情况。

解决此问题的选项是:

  1. 明确禁止在输入中使用单引号,并在构建路径时另外使用 au.Replace("'", "")。这可能不适合人名。
  2. 建立一个允许单引号的更复杂的路径。这不是微不足道的,因为 XPath 没有字符串转义机制。
  3. Use a more advanced way of defining an XPath query.

对于选项 2,假设作者 "O'Reilly",路径需要如下所示:

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

因为表达式 concat('O', "'", 'Reilly') 在 XPath 引擎中生成字符串 "O'Reilly"。以这种方式使用 concat() 是将定界引号嵌入 XPath 字符串的唯一方法。

这个函数产生这样一个表达式:

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);
    }
}

使用方法如下:

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

注意这次路径中没有单引号。