使用单引号和双引号编码XPath表达式

Rya*_*yan 27 c# xml xpath xpath-1.0

XPath(v1)无法编码表达式.

如果您只有单个OR双引号,那么您可以使用诸如的单词

//review[@name="Bob's Pizza"]
//review[@name='"Pizza" Pam']
Run Code Online (Sandbox Code Playgroud)

但是如果你有两个例如[弗雷德的"Fancy Pizza"]那么你必须在XPath(C++)中使用像Escaping Strings这样的东西来生成

//review[@name=Concat("Fred's ",'"Fancy Pizza"')]
Run Code Online (Sandbox Code Playgroud)

任何人都有c#函数来执行此操作?

一些链接很接近

编辑:一些答案建议转义'with '和',"但虽然这是有道理的,它不起作用;尝试使用XML片段:

<review name="Bob's Pizza"/>
Run Code Online (Sandbox Code Playgroud)

和xpath

//review[@name='Bob&apos;s Pizza']
Run Code Online (Sandbox Code Playgroud)

小智 11

哇,你们都肯定会让这个变得复杂.为什么不这样做呢?

public static string XpathExpression(string value)
{
    if (!value.Contains("'"))
        return '\'' + value + '\'';

    else if (!value.Contains("\""))
        return '"' + value + '"';

    else
        return "concat('" + value.Replace("'", "',\"'\",'") + "')";
}
Run Code Online (Sandbox Code Playgroud)

.NET小提琴和测试


Rob*_*ney 10

虽然它肯定不会在所有情况下都起作用,但这是一种回避问题的方法:

doc.DocumentElement.SetAttribute("searchName", name);
XmlNode n = doc.SelectNodes("//review[@name=/*/@searchName]");
Run Code Online (Sandbox Code Playgroud)

  • 我认为`/ @ foo`是错误的:要获得顶级元素的属性,你需要做`/*/@ foo`.所以谓词是`[@Name =/*/@ searchName]`. (2认同)

小智 7

我需要这个,所以我为C#创建了这个解决方案.

    /// <summary>
    /// Returns a valid XPath statement to use for searching attribute values regardless of 's or "s
    /// </summary>
    /// <param name="attributeValue">Attribute value to parse</param>
    /// <returns>Parsed attribute value in concat() if needed</returns>
    public static string GetXpathStringForAttributeValue(string attributeValue)
    {
        bool hasApos = attributeValue.Contains("'");
        bool hasQuote = attributeValue.Contains("\"");

        if (!hasApos)
        {
            return "'" + attributeValue + "'";
        }
        if (!hasQuote)
        {
            return "\"" + attributeValue + "\"";
        }

        StringBuilder result = new StringBuilder("concat(");
        StringBuilder currentArgument = new StringBuilder();
        for (int pos = 0; pos < attributeValue.Length; pos++)
        {
            switch (attributeValue[pos])
            {
                case '\'':
                    result.Append('\"');
                    result.Append(currentArgument.ToString());
                    result.Append("'\",");
                    currentArgument.Length = 0;
                    break;
                case '\"':
                    result.Append('\'');
                    result.Append(currentArgument.ToString());
                    result.Append("\"\',");
                    currentArgument.Length = 0;
                    break;
                default:
                    currentArgument.Append(attributeValue[pos]);
                    break;
            }
        }
        if (currentArgument.Length == 0)
        {
            result[result.Length - 1] = ')';
        }
        else
        {
            result.Append("'");
            result.Append(currentArgument.ToString());
            result.Append("')");
        }
        return result.ToString();
    }
Run Code Online (Sandbox Code Playgroud)


Rya*_*yan 5

这是我想出的

public static string EncaseXpathString(string input)
{         
    // If we don't have any " then encase string in "
    if (!input.Contains("\""))
        return String.Format("\"{0}\"", input);

    // If we have some " but no ' then encase in '
    if (!input.Contains("'"))
        return String.Format("'{0}'", input);

    // If we get here we have both " and ' in the string so must use Concat
    StringBuilder sb = new StringBuilder("concat(");           

    // Going to look for " as they are LESS likely than ' in our data so will minimise
    // number of arguments to concat.
    int lastPos = 0;
    int nextPos = input.IndexOf("\"");
    while (nextPos != -1)
    {
        // If this is not the first time through the loop then seperate arguments with ,
        if (lastPos != 0)
            sb.Append(",");

        sb.AppendFormat("\"{0}\",'\"'", input.Substring(lastPos, nextPos - lastPos));
        lastPos = ++nextPos;

        // Find next occurance
        nextPos = input.IndexOf("\"", lastPos);
    }

    sb.Append(")");
    return sb.ToString();
}
Run Code Online (Sandbox Code Playgroud)

使用类似的东西调用

XmlNode node = doc.SelectSingleNode("//review[@name=" + EncaseXpathString("Fred's \"Fancy Pizza\"" + "]")
Run Code Online (Sandbox Code Playgroud)

所以我们得到以下结果

EncaseXpathString("Pizza Shed") == "'Pizza Shed'";
EncaseXpathString("Bob's pizza") == "\"Bob's Pizza\"";
EncaseXpathString("\"Pizza\" Pam" == "'\"Pizza\" Pam'";
EncaseXpathString("Fred's \"Fancy Pizza\"") == "concat(\"Fred's \",'\"',\"Fancy Pizza\",'\"')";
Run Code Online (Sandbox Code Playgroud)

所以它只在必要时使用 concat(字符串中的 " 和 ' )

最后的结果显示 concat 操作没有想象的那么短(见问题),但它足够接近,任何更优化的东西都会非常复杂,因为你必须寻找匹配的“或”对。

  • 您的代码假定最后一个双引号位于输入字符串的末尾。为了覆盖最后一个双引号在其他地方的情况,行`sb.Append(")");` 应该写成`sb.AppendFormat(",\"{0}\")", input.Substring (lastPos));`。否则,干得好,感谢您的工作示例! (3认同)