C# 使用单引号和双引号编码 XPath 表达式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/642125/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Encoding XPath Expressions with both single and double quotes
提问by Ryan
XPath (v1) contains no way to encode expressions.
XPath (v1) 不包含对表达式进行编码的方法。
If you only have single OR double quotes then you can use expressions such as
如果您只有单引号或双引号,则可以使用表达式,例如
//review[@name="Bob's Pizza"]
//review[@name='"Pizza" Pam']
But if you have BOTH e.g [Fred's "Fancy Pizza"] then you have to use something like this Escaping Strings in XPath (C++)to generate
但是,如果您同时拥有 [Fred's "Fancy Pizza"],那么您必须在 XPath (C++) 中使用类似这样的转义字符串来生成
//review[@name=Concat("Fred's ",'"Fancy Pizza"')]
Anyone have a function in c# to do this?
任何人在 C# 中有一个函数来做到这一点?
Some links that are close
一些关闭的链接
- Use the MVP.XML library and XPathVariable(a very good solution but a bit heavyweight for my needs).
- Doesn't encode where both " and ' are present
- Adds more arguments to the Concat operation than is necessarye.g. would return //review[@name=concat('Fred', "'", 's ', '"', 'Fancy Pizza', '"', '')]
- 使用 MVP.XML 库和 XPathVariable(一个非常好的解决方案,但对我的需求来说有点重量级)。
- 不对 " 和 ' 都存在的地方进行编码
- 向 Concat 操作添加比必要更多的参数,例如 return //review[@name=concat('Fred', "'", 's ', '"', 'Fancy Pizza', '"', '') ]
EDIT: A few answers have suggested escaping ' with '
and " with "
but although this makes sense it does not work; try it using the XML fragment:
编辑:一些答案建议使用 ' with'
和 " with转义,"
但尽管这是有道理的,但它不起作用;尝试使用 XML 片段:
<review name="Bob's Pizza"/>
and the xpath
和 xpath
//review[@name='Bob's Pizza']
采纳答案by Kaleb
Wow, you all sure are making this complicated. Why not just do this?
哇,你们肯定让这件事变得复杂了。为什么不这样做呢?
public static string XpathExpression(string value)
{
if (!value.Contains("'"))
return '\'' + value + '\'';
else if (!value.Contains("\""))
return '"' + value + '"';
else
return "concat('" + value.Replace("'", "',\"'\",'") + "')";
}
回答by Ryan
This is what I've come up with
这是我想出的
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();
}
Called using something like
使用类似的东西调用
XmlNode node = doc.SelectSingleNode("//review[@name=" + EncaseXpathString("Fred's \"Fancy Pizza\"" + "]")
So we get the following results
所以我们得到以下结果
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\",'\"')";
So it's only using concat when its necessary (both " and ' in string)
所以它只在必要时使用 concat(字符串中的 " 和 ' )
The last result show the concat operation is not as short as it could be (see question) but its close enough and anything more optimal would be very complex as you would have to look for matching pairs of " or '.
最后的结果显示 concat 操作没有想象的那么短(见问题),但它足够接近,任何更优化的东西都会非常复杂,因为您必须寻找匹配的“或”对。
回答by Robert Rossney
Though it certainly won't work in all circumstances, here's a way to sidestep the problem:
虽然它肯定不会在所有情况下都有效,但这里有一种回避问题的方法:
doc.DocumentElement.SetAttribute("searchName", name);
XmlNode n = doc.SelectNodes("//review[@name=/*/@searchName]");
回答by Robert Rossney
I was in need of this so I created this solution, for C#.
我需要这个,所以我为 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();
}
回答by tiwahu
Another variation...my concat() part is a little lazy, but at least it uses the whole value.
另一个变体……我的 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("\"", "\",'\"',\""));
}
}
回答by Hyman
Join in the fun
加入乐趣
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
回答by Laurence Rowe
I needed to do this in XSLT itself, so came up with the following based on the answers on this page:
我需要在 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>
回答by Eric
I've had problems with all solutions so far. One has extra text sections (e.g. '"' or "'") which breaks what you're looking for. One drops all text after the last quote/dblquote which breaks as well.
到目前为止,我对所有解决方案都遇到了问题。一个有额外的文本部分(例如 '"' 或 "'"),这会破坏您正在寻找的内容。一个会删除最后一个引用/dblquote 之后的所有文本,该部分也会中断。
This is a dumb and quick solution from a dumb vb developer:
这是一个愚蠢的 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
Usage (same as previous examples):
用法(与前面的例子相同):
x.SelectNodes("/path[@attr=" & ParseXpathString(attrvalue) & "]")