Is it possible parameterize a compiled Java XPath expression ala PreparedStatement style semantics?
Asked Answered
J

3

6

Is it possible either with the stock JAX-P xpath expression engine or another compliant engine to compile an xpath expression that would allow for parametrization?

I'm looking to see if is if there is an API that would allow a developer to set placeholders within a compiled xpath and replace those values at run time.

Any insight on this as to whether or not it is possible and if there are caveats, pitfalls, or just plain "don't do that" type advice would be greatly appreciated.

(Note I corrected a "crossing of the streams" ... was having a conversation with a coworker with regard to xpath & regular expressions ... just happened to get mentally tongue tied ... sorry for the confusion)

Jeth answered 19/7, 2011 at 15:9 Comment(1)
Wow! Thanks for the answers on this. I had completely overlooked the API on this, thanks to all for pointing this out!Jeth
D
5

I'm not quite sure where "regular expression" fits into your question.

The JAXP API allows an XPath expression to contain variables (for example //emp[ssn=$e]), and the values of the variables are obtained at run-time by a call to the VariableResolver that you supply as part of the API. The JAXP spec is rather loose about what kind of values are acceptable and this may vary from one implementation to another.

The Saxon XPath API (s9api) takes this further and allows you to explicitly declare variables and their types at the time the expression is compiled, and then to supply values for the variables (of any XPath 2.0 type) when the expression is executed.

Dukas answered 19/7, 2011 at 16:47 Comment(1)
@Michael - sorry about the confusion, I have corrected my question removing the "regular" from above. Additionally, can you provide a small sample of code with your answer or a link to a page demonstrating this? I'm heading to look at the API to check it.Jeth
P
11

Here's a very simple implementation of javax.xml.xpath.XPathVariableResolver

import java.util.HashMap;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.xpath.XPathVariableResolver;

public class SimpleVariableResolver implements XPathVariableResolver {

    private static final Map<QName, Object> vars = 
        new HashMap<QName, Object>();

    public void addVariable(QName name, Object value) {
        vars.put(name, value);
    }

    public Object resolveVariable(QName name) {
        return vars.get(name);
    }

}

Use it like this:

public static void main(String[] args) throws ParserConfigurationException,
        SAXException, IOException, XPathExpressionException {

    DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = domFactory.newDocumentBuilder();
    Document doc = builder.parse("workbook.xml");

    XPath xpath = XPathFactory.newInstance().newXPath();
    SimpleVariableResolver resolver = new SimpleVariableResolver();
    resolver.addVariable(new QName(null, "id"), 2);
    xpath.setXPathVariableResolver(resolver);
    XPathExpression expr = xpath.compile("/root/element[@id=$id]");
    Object result = expr.evaluate(doc, XPathConstants.NODESET);

    NodeList nodes = (NodeList) result;
    for (int i = 0; i < nodes.getLength(); i++) {
        System.out.println(nodes.item(i).getTextContent());
    }
}

On this document:

<root>
    <element id="1">one</element>
    <element id="2">two</element>
    <element id="3">three</element>
</root>

Output:

two
Prate answered 19/7, 2011 at 17:59 Comment(2)
Thanks for posting this as well! I was implementing this pretty much the same way after reading the API.Jeth
I like @everton-agner 's answer and voted it up. This answer shows an excellent code example, but I think the subtleties are 1) xpath.compile must be called for each variable-substitution, after xpath.setXPathVariableResolver and 2) XPathVariableResolver mentions that the values each instance returns must be unchanging. So to the OP's question, I don't think this is equivalent to SQL PreparedStatement type binding.Disciplinary
D
5

I'm not quite sure where "regular expression" fits into your question.

The JAXP API allows an XPath expression to contain variables (for example //emp[ssn=$e]), and the values of the variables are obtained at run-time by a call to the VariableResolver that you supply as part of the API. The JAXP spec is rather loose about what kind of values are acceptable and this may vary from one implementation to another.

The Saxon XPath API (s9api) takes this further and allows you to explicitly declare variables and their types at the time the expression is compiled, and then to supply values for the variables (of any XPath 2.0 type) when the expression is executed.

Dukas answered 19/7, 2011 at 16:47 Comment(1)
@Michael - sorry about the confusion, I have corrected my question removing the "regular" from above. Additionally, can you provide a small sample of code with your answer or a link to a page demonstrating this? I'm heading to look at the API to check it.Jeth
T
1

AFAIK, no. I researched that some time ago and ended up using String.format().

But, this way you wouldn't be able to compile it once as a parametrized xpath expr, and that is what I think you want (and what I wanted too).

Tighe answered 19/7, 2011 at 16:19 Comment(1)
yeah pretty much what I was trying for ... oh well.Jeth

© 2022 - 2024 — McMap. All rights reserved.