如何在XPath查询中使用Java字符串变量

如何在XPath查询中使用Java字符串变量,java,xml,xpath,Java,Xml,Xpath,我有books.xml文件,其中包含作者姓名和书名。我使用以下代码片段查询books.xml XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); XPathExpression expr = xpath.compile("//book[author= 'Larry Niven']/title/text()"); 现在,如果我想在程序作为字符串变量运行时传递它,而不是直

我有
books.xml
文件,其中包含作者姓名和书名。我使用以下代码片段查询
books.xml

XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr 
    = xpath.compile("//book[author= 'Larry Niven']/title/text()");
现在,如果我想在程序作为字符串变量运行时传递它,而不是直接将名称放入查询中,那么该如何做呢。仅仅输入字符串变量名是行不通的

String rawXPath = "//book[author= '" + larrysName + "']/title/text()";


其中,
larrysName
是来自某处的
String
类型的变量。

如果您想要现成的实现,可以使用支持变量声明的commons:
这里的问题是当你有一个像臭名昭著的拉里·巴希尔·奥尼文这样的作家时

在这种情况下,您将需要转义变量,就像在这个简单的实现中一样:

  public static String escape(String s) {
    Matcher matcher = Pattern.compile("['\"]")
        .matcher(s);
    StringBuilder buffer = new StringBuilder("concat(");
    int start = 0;
    while (matcher.find()) {
      buffer.append("'")
          .append(s.substring(start, matcher.start()))
          .append("',");
      buffer.append("'".equals(matcher.group()) ? "\"'\"," : "'\"',");
      start = matcher.end();
    }
    if (start == 0) {
      return "'" + s + "'";
    }
    return buffer.append("'")
        .append(s.substring(start))
        .append("'")
        .append(")")
        .toString();
  }
这可以通过以下代码进行演示:

String xml =
    "<xml><foo bar=\"Larry &quot;Basher&quot; O'Niven\">Ringworm</foo></xml>";
String query =
    String.format("//foo[@bar=%s]", escape("Larry \"Basher\" O'Niven"));
System.out.println(query);
String book = XPathFactory.newInstance()
    .newXPath()
    .evaluate(query, new InputSource(new StringReader(xml)));
System.out.println(query + " > " + book);
stringxml=
“癣”;
字符串查询=
String.format(“//foo[@bar=%s]”,escape(“拉里\“Basher \“O'Niven”);
System.out.println(查询);
String book=XPathFactory.newInstance()
.newXPath()
.evaluate(查询、新输入源(新StringReader(xml)));
System.out.println(查询+“>”+书本);

事实上,您可以在XPath中同时使用自定义函数和变量,但对于许多用途来说,快速破解可能更有效

下面是我为学生开发的学习工具代码。它允许您执行以下操作:

// create some variable we want to use in the xpath
xPathVariableAndFunctionResolver.newVariable("myNamespace", "id", "xs:string", "l2"); // myNamespace is declared in the namespace context with prefix 'my'

// create an XPath expression
String expression = "//did:Component[@id=$my:id]"; // variable $namespace:name
XPathExpression findComponents = xPathFunctionAndVariableOperator.compile(expression);

// execute the XPath expression against the document
NodeList statements = (NodeList)findComponents.evaluate(document, XPathConstants.NODESET);
XPath函数也是如此。代码首先是普通XPath求值的包装器:

public class XPathOperator {

    protected XPath xPath;
    protected XPathFactory xPathFactory;

    private Hashtable<String, XPathExpression> compiled = new Hashtable<String, XPathExpression>();

    protected void initFactory() throws XPathFactoryConfigurationException {
        xPathFactory = XPathFactory.newInstance(XPathConstants.DOM_OBJECT_MODEL);
    }

    protected void initXPath(NamespaceContext context) {
        xPath = xPathFactory.newXPath();
        xPath.setNamespaceContext(context);
    }

    public XPathOperator(NamespaceContext context) throws XPathFactoryConfigurationException {
        initFactory();
        initXPath(context);
    }

    public Object evaluate(Document document, String expression, QName value) throws XPathExpressionException {

        // create an XPath expression - http://www.zvon.org/xxl/XPathTutorial/General/examples.html
        XPathExpression findStatements = compile(expression);

        // execute the XPath expression against the document
        return (NodeList)findStatements.evaluate(document, value);
    }

    public XPathExpression compile(String expression) throws XPathExpressionException {
        if(compiled.containsKey(expression)) {
            return (XPathExpression) compiled.get(expression);
        }

        XPathExpression xpath = xPath.compile(expression);

        System.out.println("Compiled XPath " + expression);

        compiled.put(expression, xpath);

        return xpath;
    }
}
如果没有变量和函数解析器,这将不会有多大乐趣:

public class XPathVariableAndFunctionResolver implements XPathVariableResolver, XPathFunctionResolver {

    private Hashtable functions = new Hashtable();
    private Hashtable variables = new Hashtable();

    private SchemaDVFactory factory = SchemaDVFactory.getInstance();

    public XPathFunction resolveFunction(QName functionName, int arity) {
        Hashtable table = (Hashtable)functions.get(functionName.getNamespaceURI());
        if(table != null) {
            XPathFunction function = (XPathFunction)table.get(functionName.getLocalPart());
            if(function == null) {
                throw new RuntimeException("Function " + functionName.getLocalPart() + " does not exist in namespace " + functionName.getNamespaceURI() + "!");
            }
            System.out.println("Resolved function " + functionName + " with " + arity + " argument(s)");
            return function;
        }
        throw new RuntimeException("Function namespace " + functionName.getNamespaceURI() + " does not exist!");
    }

    /**
     *
     * Adds a variable using namespace and name, primitive type and default value
     *
     * @param namespace
     * @param name
     * @param datatype      one of the built-in XML datatypes
     * @param value
     * @throws InvalidDatatypeValueException    if value is not of correct datatype
     */

    @SuppressWarnings("unchecked")
    public void newVariable(String namespace, String name, String datatype, String value) throws InvalidDatatypeValueException {

        int index = datatype.indexOf(":");
        if(index != -1) {
            datatype = datatype.substring(index+1);
        }
        XSSimpleType builtInType = factory.getBuiltInType(datatype);

        if(builtInType == null) {
            throw new RuntimeException("Null type for " + datatype);
        }

        ValidationState validationState = new ValidationState();
        ValidatedInfo validatedInfo = new ValidatedInfo();

        builtInType.validate(value, validationState, validatedInfo);

        System.out.println("Defined variable " + name + " as " + datatype + " with value " + value);

        Hashtable table;
        if(!variables.containsKey(namespace)) {
            table = new Hashtable();
            variables.put(namespace, table);
        } else {
            table = (Hashtable)variables.get(namespace);
        }

        table.put(name, new Object[]{validatedInfo, builtInType});
    }

    public void newVariableValue(String namespace, String name, String value) throws InvalidDatatypeValueException {
        ValidationState validationState = new ValidationState();

        Hashtable table;
        if(!variables.containsKey(namespace)) {
            throw new RuntimeException("Unknown variable namespace " + namespace);
        } else {
            table = (Hashtable)variables.get(namespace);
        }

        Object[] bundle = (Object[])table.get(name);
        ValidatedInfo validatedInfo = (ValidatedInfo)bundle[0];
        XSSimpleType builtInType =  (XSSimpleType)bundle[1];
        builtInType.validate(value, validationState, validatedInfo); // direct reference transfer of value

        System.out.println("Assigned value " + validatedInfo.normalizedValue + " to variable " + name);
    }

    public Object resolveVariable(QName variableName) {

        Hashtable table;
        if(!variables.containsKey(variableName.getNamespaceURI())) {
            throw new RuntimeException("Unknown variable namespace " + variableName.getNamespaceURI());
        } else {
            table = (Hashtable)variables.get(variableName.getNamespaceURI());
        }

        Object[] bundle = (Object[])table.get(variableName.getLocalPart());
        if(bundle != null) {
            ValidatedInfo var = (ValidatedInfo)bundle[0];

            if(var != null) {
                switch(var.actualValueType) { // some types omitted, customize your own
                case XSConstants.INTEGER_DT:
                case XSConstants.DECIMAL_DT:
                case XSConstants.INT_DT:
                case XSConstants.LONG_DT:
                case XSConstants.SHORT_DT:
                case XSConstants.BYTE_DT:
                case XSConstants.UNSIGNEDBYTE_DT:
                case XSConstants.UNSIGNEDINT_DT:
                case XSConstants.UNSIGNEDLONG_DT:
                case XSConstants.UNSIGNEDSHORT_DT:
                    return new Integer(var.normalizedValue);
                case XSConstants.DATE_DT:
                case XSConstants.DATETIME_DT:
                case XSConstants.GDAY_DT:
                case XSConstants.GMONTH_DT:
                case XSConstants.GMONTHDAY_DT:
                case XSConstants.GYEAR_DT:
                case XSConstants.GYEARMONTH_DT:
                case XSConstants.DURATION_DT:
                case XSConstants.TIME_DT:
                    return new Date(var.normalizedValue);
                case XSConstants.FLOAT_DT:
                    return new Float(Float.parseFloat(var.normalizedValue));
                case XSConstants.DOUBLE_DT:
                    return new Double(Double.parseDouble(var.normalizedValue));
                case XSConstants.STRING_DT:
                case XSConstants.QNAME_DT:
                    return var.normalizedValue;
                default:
                    throw new RuntimeException("Unknown datatype " + var.actualValueType + " for variable " + variableName + " in namespace " + variableName.getNamespaceURI());
                }
            }
        }
        throw new RuntimeException("Could not resolve value " + variableName + " in namespace " + variableName.getNamespaceURI());
    }

    public void addFunction(String namespace, String name, XPathFunction function) {
        Hashtable table;
        if(!functions.containsKey(namespace)) {
            table = new Hashtable();
            functions.put(namespace, table);
        } else {
            table = (Hashtable)functions.get(namespace);
        }
        table.put(name, function);
    }

}
这些函数显然不能包含在上面的代码中,因为它们通常运行自定义代码(即,关键是您要编写自己的类),所以请使用下面的代码

public abstract class XPathFunctionImpl implements XPathFunction {

    /**
     * This function is called by the XPath expression as it implements the interface XPathFunction
     */

    protected int numberArguments;

    public Object evaluate(List args) throws XPathFunctionException {
        if(args.size() == numberArguments) {
            return evaluateImpl(args);
        }
        throw new RuntimeException("Illegal number of arguments for " + this);
    }

    public abstract Object evaluateImpl(List args) throws XPathFunctionException;

}
然后在evaluateImpl(..)中实现/子类化您自己的逻辑


这肯定会使字符串的附加看起来非常。。。有吸引力;)注意:这段代码已经有好几年的历史了,可能有更好的方法来完成这一切。

为什么不能动态构建xpath表达式,我的意思是“//book[author=”“+authorName+”]/title/text()”?是的,这是一个很好的开始。我也试试。谢谢。如何将变量注册到XPath计算上下文中完全取决于XPath引擎API。-1不幸的是,对于像O'Hara或任何其他包含引号的文本这样的名称,这将无法完美地工作。-1。出于同样的原因,它也容易受到XPath注入的攻击——在实践中可能没有SQL注入那么糟糕,但基于相同的原则。
public class XPathVariableAndFunctionResolver implements XPathVariableResolver, XPathFunctionResolver {

    private Hashtable functions = new Hashtable();
    private Hashtable variables = new Hashtable();

    private SchemaDVFactory factory = SchemaDVFactory.getInstance();

    public XPathFunction resolveFunction(QName functionName, int arity) {
        Hashtable table = (Hashtable)functions.get(functionName.getNamespaceURI());
        if(table != null) {
            XPathFunction function = (XPathFunction)table.get(functionName.getLocalPart());
            if(function == null) {
                throw new RuntimeException("Function " + functionName.getLocalPart() + " does not exist in namespace " + functionName.getNamespaceURI() + "!");
            }
            System.out.println("Resolved function " + functionName + " with " + arity + " argument(s)");
            return function;
        }
        throw new RuntimeException("Function namespace " + functionName.getNamespaceURI() + " does not exist!");
    }

    /**
     *
     * Adds a variable using namespace and name, primitive type and default value
     *
     * @param namespace
     * @param name
     * @param datatype      one of the built-in XML datatypes
     * @param value
     * @throws InvalidDatatypeValueException    if value is not of correct datatype
     */

    @SuppressWarnings("unchecked")
    public void newVariable(String namespace, String name, String datatype, String value) throws InvalidDatatypeValueException {

        int index = datatype.indexOf(":");
        if(index != -1) {
            datatype = datatype.substring(index+1);
        }
        XSSimpleType builtInType = factory.getBuiltInType(datatype);

        if(builtInType == null) {
            throw new RuntimeException("Null type for " + datatype);
        }

        ValidationState validationState = new ValidationState();
        ValidatedInfo validatedInfo = new ValidatedInfo();

        builtInType.validate(value, validationState, validatedInfo);

        System.out.println("Defined variable " + name + " as " + datatype + " with value " + value);

        Hashtable table;
        if(!variables.containsKey(namespace)) {
            table = new Hashtable();
            variables.put(namespace, table);
        } else {
            table = (Hashtable)variables.get(namespace);
        }

        table.put(name, new Object[]{validatedInfo, builtInType});
    }

    public void newVariableValue(String namespace, String name, String value) throws InvalidDatatypeValueException {
        ValidationState validationState = new ValidationState();

        Hashtable table;
        if(!variables.containsKey(namespace)) {
            throw new RuntimeException("Unknown variable namespace " + namespace);
        } else {
            table = (Hashtable)variables.get(namespace);
        }

        Object[] bundle = (Object[])table.get(name);
        ValidatedInfo validatedInfo = (ValidatedInfo)bundle[0];
        XSSimpleType builtInType =  (XSSimpleType)bundle[1];
        builtInType.validate(value, validationState, validatedInfo); // direct reference transfer of value

        System.out.println("Assigned value " + validatedInfo.normalizedValue + " to variable " + name);
    }

    public Object resolveVariable(QName variableName) {

        Hashtable table;
        if(!variables.containsKey(variableName.getNamespaceURI())) {
            throw new RuntimeException("Unknown variable namespace " + variableName.getNamespaceURI());
        } else {
            table = (Hashtable)variables.get(variableName.getNamespaceURI());
        }

        Object[] bundle = (Object[])table.get(variableName.getLocalPart());
        if(bundle != null) {
            ValidatedInfo var = (ValidatedInfo)bundle[0];

            if(var != null) {
                switch(var.actualValueType) { // some types omitted, customize your own
                case XSConstants.INTEGER_DT:
                case XSConstants.DECIMAL_DT:
                case XSConstants.INT_DT:
                case XSConstants.LONG_DT:
                case XSConstants.SHORT_DT:
                case XSConstants.BYTE_DT:
                case XSConstants.UNSIGNEDBYTE_DT:
                case XSConstants.UNSIGNEDINT_DT:
                case XSConstants.UNSIGNEDLONG_DT:
                case XSConstants.UNSIGNEDSHORT_DT:
                    return new Integer(var.normalizedValue);
                case XSConstants.DATE_DT:
                case XSConstants.DATETIME_DT:
                case XSConstants.GDAY_DT:
                case XSConstants.GMONTH_DT:
                case XSConstants.GMONTHDAY_DT:
                case XSConstants.GYEAR_DT:
                case XSConstants.GYEARMONTH_DT:
                case XSConstants.DURATION_DT:
                case XSConstants.TIME_DT:
                    return new Date(var.normalizedValue);
                case XSConstants.FLOAT_DT:
                    return new Float(Float.parseFloat(var.normalizedValue));
                case XSConstants.DOUBLE_DT:
                    return new Double(Double.parseDouble(var.normalizedValue));
                case XSConstants.STRING_DT:
                case XSConstants.QNAME_DT:
                    return var.normalizedValue;
                default:
                    throw new RuntimeException("Unknown datatype " + var.actualValueType + " for variable " + variableName + " in namespace " + variableName.getNamespaceURI());
                }
            }
        }
        throw new RuntimeException("Could not resolve value " + variableName + " in namespace " + variableName.getNamespaceURI());
    }

    public void addFunction(String namespace, String name, XPathFunction function) {
        Hashtable table;
        if(!functions.containsKey(namespace)) {
            table = new Hashtable();
            functions.put(namespace, table);
        } else {
            table = (Hashtable)functions.get(namespace);
        }
        table.put(name, function);
    }

}
public abstract class XPathFunctionImpl implements XPathFunction {

    /**
     * This function is called by the XPath expression as it implements the interface XPathFunction
     */

    protected int numberArguments;

    public Object evaluate(List args) throws XPathFunctionException {
        if(args.size() == numberArguments) {
            return evaluateImpl(args);
        }
        throw new RuntimeException("Illegal number of arguments for " + this);
    }

    public abstract Object evaluateImpl(List args) throws XPathFunctionException;

}