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

时间:2009-03-13 10:42:00

标签: c# xml xpath xpath-1.0

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

如果您只有单个或双引号,那么您可以使用

等表达式
//review[@name="Bob's Pizza"]
//review[@name='"Pizza" Pam']

但如果你有[弗雷德的“Fancy Pizza”]那么你必须使用这样的Escaping Strings in XPath (C++)来生成

//review[@name=Concat("Fred's ",'"Fancy Pizza"')]

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

一些关闭的链接

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

<review name="Bob's Pizza"/>

和xpath

//review[@name='Bob&apos;s Pizza']

9 个答案:

答案 0 :(得分:10)

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

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

答案 1 :(得分:9)

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

$('your-select-element').selectUtils('setEmpty');

.NET Fiddle & test

答案 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();
    }

答案 3 :(得分:4)

这就是我提出的

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

使用

之类的东西调用
XmlNode node = doc.SelectSingleNode("//review[@name=" + EncaseXpathString("Fred's \"Fancy Pizza\"" + "]")

所以我们得到以下结果

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\",'\"')";

所以它只在必要时使用concat(“和”在字符串中)

最后的结果显示concat操作并不尽可能短(请参阅问题)但是它足够接近并且任何更优化都会非常复杂,因为您必须寻找匹配的“或”对。

答案 4 :(得分:4)

到目前为止,我遇到了所有解决方案的问题。一个有额外的文本部分(例如'“'或”'“)可以打破你正在寻找的东西。一个文本在最后一个引用/ dblquote之后删除了所有文本。

这是一个愚蠢的vb开发人员的愚蠢而快速的解决方案:

Function ParseXpathString(ByVal input As String) As String
    input = Replace(input, "'", Chr(1))
    input = Replace(input, """", Chr(2))
    input = Replace(input, Chr(1), "',""'"",'")
    input = Replace(input, Chr(2), "','""','")
    input = "concat('','" + input + "')"
    Return input
End Function

用法(与前面的例子相同):

x.SelectNodes("/path[@attr=" & ParseXpathString(attrvalue) & "]")

答案 5 :(得分:2)

我需要在XSLT本身中执行此操作,因此根据此页面上的答案提出以下内容:

<xsl:template name="escape-string">
  <xsl:param name="string"/>
  <xsl:param name="concat" select="true()"/>
  <xsl:variable name="quote">"</xsl:variable>
  <xsl:variable name="apos">'</xsl:variable>
  <xsl:choose>
    <xsl:when test="not(contains($string, $apos))">'<xsl:value-of select="$string"/>'</xsl:when>
    <xsl:when test="not(contains($string, $quote))">"<xsl:value-of select="$string"/>"</xsl:when>
    <xsl:otherwise>
      <xsl:if test="$concat">concat(</xsl:if>
      <xsl:call-template name="escape-string">
        <xsl:with-param name="string" select="substring-before($string, $apos)"/>
        <xsl:with-param name="concat" select="false()"/>
      </xsl:call-template>
      <xsl:text>, "'", </xsl:text>
      <xsl:call-template name="escape-string">
        <xsl:with-param name="string" select="substring-after($string, $apos)"/>
        <xsl:with-param name="concat" select="false()"/>
      </xsl:call-template>
      <xsl:if test="$concat">)</xsl:if>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

答案 6 :(得分:1)

另一种变化......我的concat()部分有点懒,但至少它使用整个值。

    /// <summary>
    /// Returns an XPath string literal to use for searching attribute values (wraped in apostrophes, quotes, or as a concat function).
    /// </summary>
    /// <param name="attributeValue">Attribute value to encode and wrap.</param>
    public static string CreateXpathLiteral(string attributeValue)
    {
        if (!attributeValue.Contains("\""))
        {
            // if we don't have any quotes, then wrap string in quotes...
            return string.Format("\"{0}\"", attributeValue);
        }
        else if (!attributeValue.Contains("'"))
        {
            // if we have some quotes, but no apostrophes, then wrap in apostrophes...
            return string.Format("'{0}'", attributeValue);
        }
        else
        {
            // must use concat so the literal in the XPath will find a match...
            return string.Format("concat(\"{0}\")", attributeValue.Replace("\"", "\",'\"',\""));
        }
    }

答案 7 :(得分:1)

加入乐趣

public string XPathLiteral(string text) {

    const string APOS = "'";
    const string QUOTE = @"""";

    int pos = 0;

    int posApos;
    int posQuote;

    posQuote = text.IndexOf(QUOTE, pos);

    if (posQuote < 0) {
        return QUOTE + text + QUOTE;
    }//if

    posApos = text.IndexOf(APOS, pos);

    if (posApos < 0) {
        return APOS + text + APOS;
    }//if

    bool containsApos = posApos < posQuote;

    StringBuilder sb = new StringBuilder("concat(", text.Length * 2);

    bool loop = true;
    bool comma = false;

    while (loop) {

        if (posApos < 0) {
            posApos = text.Length;
            loop = false;
        }//if

        if (posQuote < 0) {
            posQuote = text.Length;
            loop = false;
        }//if

        if (comma) {
            sb.Append(",");
        } else {
            comma = true;
        }//if

        if (containsApos) {
            sb.Append(QUOTE);
            sb.Append(text.Substring(pos, posQuote - pos));
            sb.Append(QUOTE);
            pos = posQuote;
            if (loop) posApos = text.IndexOf(APOS, pos + 1);
        } else {
            sb.Append(APOS);
            sb.Append(text.Substring(pos, posApos - pos));
            sb.Append(APOS);
            pos = posApos;
            if (loop) posQuote = text.IndexOf(QUOTE, pos + 1);
        }//if

        // Toggle
        containsApos = !containsApos;

    }//while

    sb.Append(")");

    return sb.ToString();

}//method

答案 8 :(得分:-1)

网址:http://vaibhavgaikwad.wordpress.com/2007/10/04/handling-apostrophe-single-quote-in-xpath-expressions-in-net/

报价:

  public static string GetXPathString(string input) {
    string[] fragments = input.Split(new char[] { ‘\” });
    string result = “”;
    result += “concat(””;
    for (int i = 0; i < fragments.Length; i++)
    {
      result += “, ‘” + fragments[i] + “‘”;
      if (i < fragments.Length - 1)
      {
        result += “, \”‘\”";
      }
    }
    result += “)”;
    return result;
  }

以下是修改上述代码以便使用我们的新功能的方法: //记得在=和]之后删除单引号

XmlNode n = doc.SelectSingleNode(“/root/emp[@lname=" + GetXPathString(ln) + "]“);

或者只是像之前的帖子那样建议。使用&amp;和&amp; quot;

替换'和'