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)

Michael Petrotta
  • 59,888
  • 27
  • 145
  • 179
Dave G
  • 9,639
  • 36
  • 41
  • Wow! Thanks for the answers on this. I had completely overlooked the API on this, thanks to all for pointing this out! – Dave G Jul 19 '11 at 18:04

3 Answers3

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
Wayne
  • 59,728
  • 15
  • 131
  • 126
  • Thanks for posting this as well! I was implementing this pretty much the same way after reading the API. – Dave G Jul 19 '11 at 18:05
  • 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. – javabrett May 04 '16 at 09:59
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.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • @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. – Dave G Jul 19 '11 at 17:40
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).

javabrett
  • 7,020
  • 4
  • 51
  • 73
everton
  • 7,579
  • 2
  • 29
  • 42