Passing parameters to an XQuery or XSLT through an XProc Pipeline

(syndicated from original posting on the EMC Community Network)

I’ve recently been helping someone get started with the XML REST Framework, and they asked about getting query string parameters into the XQueries or XSLTs that were a part of their resource implementation.  For those of you who are not familiar, what the XML REST Framework does is leverages Spring MVC for all of the HTTP protocol stuff, a developer builds a very thin Java shim to an XProc pipeline that implements the RESTful service.  The post I link to above and two earlier versions explain this all in more detail.  What I am focusing on in this post is how I can get values from the query string into the right places within my pipeline; there are two parts to this.  1) you need to get your hands on the query string argument and 2) you need to pass that into the XProc pipeline in such a way that it gets to the XQuery or XSLT.  It turns out that #2 was already demonstrated in the Patients.java class file in combination with the resourceGET.xpl.  Let’s drill in on that a little bit.

If you take a look at the top of the xpl you see the following code – really declarations of the inputs and outputs for the pipeline.

<p:declare-step name="main" xmlns:p="http://www.w3.org/ns/xproc"
     xmlns:c="http://www.w3.org/ns/xproc-step" version="1.0">
   <p:input port='xqueryscript' />
   <p:input port="stylesheet"/>
   <p:input port="stylesheetParameters" kind="parameter"/>
   <p:input port="xqueryParameters" kind="parameter"/>
   <p:output port='result' sequence='true' primary='true' />
   <p:output port='error' sequence="true">
      <p:pipe step='checkXquery' port='error' />
   </p:output>
…

You’ll note two different input ports for parameters, one called stylesheetParameters and the other xqueryParameters; these were names of my choosing (just like parameters in any other language would be.) A parameter input port is named and the value is a list of keyword value pairs; that’s right, a parameter input port actually carries a set of key/value pairs (Norm Walsh, XML guru and one of the lead authors on the XProc spec says “[XProc] parameters suck.” – they are kinda weird and we are thinking about changing things in the next version of XProc).  In this example, I built the pipeline to keep the XQuery parameters separate from the XSLT ones; if I wanted to have a key/value pair passed down into both my XQuery and my XSLT, at the java level I could just add that pair to both parameters.  So speaking of that, our framework allows you to add parameters to the pipeline input with a call to the addParameter method:

pi.addParameter("stylesheetParameters", new QName("baseURL"), request.getRequestURL().toString());

Going back to the xpl then, let’s look at how the input parameters to the pipeline make their way down into the XQuery or XSLT. In each case, we just pass the parameter from the main pipeline down into the appropriate step.  XProc is nice that way and while the current parameter design might feel a bit odd, it does enable this easy, easy approach.

For XQuery:

<p:xquery name="xquery">
   <p:input port='source'>
      <p:document href="xhive:/" />
   </p:input>
   <p:input port="query">
      <p:pipe step="main" port="xqueryscript" />
   </p:input>
   <p:input port="parameters">
      <p:pipe step='main' port='xqueryParameters'/>
   </p:input>
</p:xquery>

And for XSLT:

<p:xslt>
   <p:input port='source'>
      <p:pipe step='xquery' port='result'/>
   </p:input>
   <p:input port='stylesheet'>
      <p:pipe step='main' port='stylesheet'/>
   </p:input>
   <p:input port='parameters'>
      <p:pipe step='main' port='stylesheetParameters'/>
   </p:input>
</p:xslt>

So then the only thing that remains is how to get your hands on the query string argument (#1 of the two things we needed to do) and because our framework uses Sprint MVC for our REST protocol layer, this is in the Java code. The following method could be added to the Patients.java class of the sample application, to show this:

@RequestMapping(method = RequestMethod.GET, value="/alt")
@ResponseStatus(HttpStatus.OK)
public String getPatientAlt(HttpServletRequest request, HttpServletResponse response, Model model, @RequestParam("pid") String pid)
 throws XProcException, TransformerException, IOException {

  try {

    PipelineInputCache pi = new PipelineInputCache();
       // pass the patient ID into the pipeline for use in the xQuery (to look up the right patient)
    pi.addParameter("xqueryParameters", new QName("pid"), pid);
       // supply current resource URL as the base URL to craft hyperlinks
    pi.addParameter("stylesheetParameters", new QName("baseURL"), request.getRequestURL().toString());

    PipelineOutput output = m_getPatient.executeOn(pi);

    model.addAttribute("pipelineOutput", output);
       return "pipelineOutput";
  } finally {
       ;
  }
}

The ONLY difference between this and the original getPatient method is where I get the value for the patient ID that I will pass down into the XQuery.  The way that Sprint MVC does this is with the @RequestParam annotation.  You can see that for testing I changed the @RequestMapping path to be /alt so the URL that will invoke this code is something of the form:

http://example.com/XProcPatientServiceMVC/patients/alt?pid=12345

Note that I didn’t even have to create a new XProcXMLProcessingContext because I just reused all of the implementation from the original get patient – I’m just getting the value for the passed in parameter from somewhere else.

Share Your Thoughts