Rob*_*ney 58
这非常难以做到.
看看XPath建议书,你会发现它将文字定义为:
Literal ::= '"' [^"]* '"'
| "'" [^']* "'"
Run Code Online (Sandbox Code Playgroud)
也就是说,XPath表达式中的字符串文字可以包含撇号或双引号,但不能同时包含两者.
你无法使用转义来解决这个问题.像这样的文字:
'Some'Value'
Run Code Online (Sandbox Code Playgroud)
将匹配此XML文本:
Some'Value
Run Code Online (Sandbox Code Playgroud)
这意味着有可能存在一段XML文本无法生成要匹配的XPath文字,例如:
<elm att=""&apos"/>
Run Code Online (Sandbox Code Playgroud)
但这并不意味着将该文本与XPath匹配是不可能的,这只是棘手的.在您尝试匹配的值包含单引号和双引号的任何情况下,您都可以构造一个表达式,用于concat生成它将匹配的文本:
elm[@att=concat('"', "'")]
Run Code Online (Sandbox Code Playgroud)
所以这导致我们这个,这比我想要的要复杂得多:
/// <summary>
/// Produce an XPath literal equal to the value if possible; if not, produce
/// an XPath expression that will match the value.
///
/// Note that this function will produce very long XPath expressions if a value
/// contains a long run of double quotes.
/// </summary>
/// <param name="value">The value to match.</param>
/// <returns>If the value contains only single or double quotes, an XPath
/// literal equal to the value. If it contains both, an XPath expression,
/// using concat(), that evaluates to the value.</returns>
static string XPathLiteral(string value)
{
// if the value contains only single or double quotes, construct
// an XPath literal
if (!value.Contains("\""))
{
return "\"" + value + "\"";
}
if (!value.Contains("'"))
{
return "'" + value + "'";
}
// if the value contains both single and double quotes, construct an
// expression that concatenates all non-double-quote substrings with
// the quotes, e.g.:
//
// concat("foo", '"', "bar")
StringBuilder sb = new StringBuilder();
sb.Append("concat(");
string[] substrings = value.Split('\"');
for (int i = 0; i < substrings.Length; i++ )
{
bool needComma = (i>0);
if (substrings[i] != "")
{
if (i > 0)
{
sb.Append(", ");
}
sb.Append("\"");
sb.Append(substrings[i]);
sb.Append("\"");
needComma = true;
}
if (i < substrings.Length - 1)
{
if (needComma)
{
sb.Append(", ");
}
sb.Append("'\"'");
}
}
sb.Append(")");
return sb.ToString();
}
Run Code Online (Sandbox Code Playgroud)
是的,我用所有边缘情况测试了它.这就是逻辑如此愚蠢复杂的原因:
foreach (string s in new[]
{
"foo", // no quotes
"\"foo", // double quotes only
"'foo", // single quotes only
"'foo\"bar", // both; double quotes in mid-string
"'foo\"bar\"baz", // multiple double quotes in mid-string
"'foo\"", // string ends with double quotes
"'foo\"\"", // string ends with run of double quotes
"\"'foo", // string begins with double quotes
"\"\"'foo", // string begins with run of double quotes
"'foo\"\"bar" // run of double quotes in mid-string
})
{
Console.Write(s);
Console.Write(" = ");
Console.WriteLine(XPathLiteral(s));
XmlElement elm = d.CreateElement("test");
d.DocumentElement.AppendChild(elm);
elm.SetAttribute("value", s);
string xpath = "/root/test[@value = " + XPathLiteral(s) + "]";
if (d.SelectSingleNode(xpath) == elm)
{
Console.WriteLine("OK");
}
else
{
Console.WriteLine("Should have found a match for {0}, and didn't.", s);
}
}
Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)
编辑:经过繁重的单元测试会议,并检查XPath标准,我修改了我的功能如下:
public static string ToXPath(string value) {
const string apostrophe = "'";
const string quote = "\"";
if(value.Contains(quote)) {
if(value.Contains(apostrophe)) {
throw new XPathException("Illegal XPath string literal.");
} else {
return apostrophe + value + apostrophe;
}
} else {
return quote + value + quote;
}
}
Run Code Online (Sandbox Code Playgroud)
似乎XPath根本没有一个字符转义系统,它真的非常原始.显然我的原始代码只是巧合.我为误导任何人而道歉!
以下原始答案仅供参考 - 请忽略
为安全起见,请确保转义XPath字符串中所有5个预定义XML实体,例如
public static string ToXPath(string value) {
return "'" + XmlEncode(value) + "'";
}
public static string XmlEncode(string value) {
StringBuilder text = new StringBuilder(value);
text.Replace("&", "&");
text.Replace("'", "'");
text.Replace(@"""", """);
text.Replace("<", "<");
text.Replace(">", ">");
return text.ToString();
}
Run Code Online (Sandbox Code Playgroud)
我以前做过这个,它工作正常.如果它对你不起作用,也许你需要让我们知道这个问题的其他背景.
我移植了Robert对Java的回答(在1.6中测试过):
/// <summary>
/// Produce an XPath literal equal to the value if possible; if not, produce
/// an XPath expression that will match the value.
///
/// Note that this function will produce very long XPath expressions if a value
/// contains a long run of double quotes.
/// </summary>
/// <param name="value">The value to match.</param>
/// <returns>If the value contains only single or double quotes, an XPath
/// literal equal to the value. If it contains both, an XPath expression,
/// using concat(), that evaluates to the value.</returns>
public static String XPathLiteral(String value) {
if(!value.contains("\"") && !value.contains("'")) {
return "'" + value + "'";
}
// if the value contains only single or double quotes, construct
// an XPath literal
if (!value.contains("\"")) {
System.out.println("Doesn't contain Quotes");
String s = "\"" + value + "\"";
System.out.println(s);
return s;
}
if (!value.contains("'")) {
System.out.println("Doesn't contain apostophes");
String s = "'" + value + "'";
System.out.println(s);
return s;
}
// if the value contains both single and double quotes, construct an
// expression that concatenates all non-double-quote substrings with
// the quotes, e.g.:
//
// concat("foo", '"', "bar")
StringBuilder sb = new StringBuilder();
sb.append("concat(");
String[] substrings = value.split("\"");
for (int i = 0; i < substrings.length; i++) {
boolean needComma = (i > 0);
if (!substrings[i].equals("")) {
if (i > 0) {
sb.append(", ");
}
sb.append("\"");
sb.append(substrings[i]);
sb.append("\"");
needComma = true;
}
if (i < substrings.length - 1) {
if (needComma) {
sb.append(", ");
}
sb.append("'\"'");
}
System.out.println("Step " + i + ": " + sb.toString());
}
//This stuff is because Java is being stupid about splitting strings
if(value.endsWith("\"")) {
sb.append(", '\"'");
}
//The code works if the string ends in a apos
/*else if(value.endsWith("'")) {
sb.append(", \"'\"");
}*/
sb.append(")");
String s = sb.toString();
System.out.println(s);
return s;
}
Run Code Online (Sandbox Code Playgroud)
希望这有助于某人!
到目前为止,解决此问题的最佳方法是使用XPath库提供的工具来声明可以在表达式中引用的XPath级变量.然后,变量值可以是宿主编程语言中的任何字符串,并且不受XPath字符串文字的限制.例如,在Java中javax.xml.xpath:
XPathFactory xpf = XPathFactory.newInstance();
final Map<String, Object> variables = new HashMap<>();
xpf.setXPathVariableResolver(new XPathVariableResolver() {
public Object resolveVariable(QName name) {
return variables.get(name.getLocalPart());
}
});
XPath xpath = xpf.newXPath();
XPathExpression expr = xpath.compile("ListObject[@Title=$val]");
variables.put("val", someValue);
NodeList nodes = (NodeList)expr.evaluate(someNode, XPathConstants.NODESET);
Run Code Online (Sandbox Code Playgroud)
对于C#,XPathNavigator您将定义XsltContext 本MSDN文章中描述的自定义(您只需要此示例中与变量相关的部分,而不是扩展函数).
| 归档时间: |
|
| 查看次数: |
36566 次 |
| 最近记录: |