Livecycle has some useful Service components for manipulating files, database rows and XML documents. And while XSLT is a powerful and useful language, it can sometimes be a little unwieldy to express simple logical problems.
I recently had to perform some simple logical programming on the server side to select one of three text fields on a form. The text fields were comments from users and the last user to touch the form before exiting the flow had their comments inserted into an email back to the form originator. There are a multitude of different ways of handling this problem on the form instead of on the server, however, this problem came to me as a system maintainer and there were constraints I had to observe.
Avoka’s XQuery component makes it possible to construct strings and XML fragments using the XQuery language. XQuery, as you may know, has a syntactic structure called FLWOR or
This is typified by the example given on W3Schools using the Books XML data sample:
for $x in doc("books.xml")/bookstore/book where $x/price>30 order by $x/title return $x/title
*Note that the Let is implied in $x in doc(…)/bookstore/book
But what I needed was not an iterative solution (at least, not one that required a for loop), but a way to simply express the conditional logic to select the first non-empty XML text node.
To test the hierarchy of user’s for the last commenter, I could have used a number of Decision and Set components, but why take 3 steps when you can take 1?
Alternately, I could have implemented similar logic using the Livecycle script component and while it would have performed in a single workflow step, the script component can be unwieldy when manipulating form values. Do you really want to …
… just to test a couple of values?
Step 1: Workflow
There are two possible types of invocation on the XQuery Service Component, Single Document and Merge Document. The Merge Document invocation is intended to be more powerful so in this example we’ll be considering the Single Document invocation of the XQuery component.
I inserted the XQuery component (Last Commenter) into the workflow, just before the email step to notify the originator of the last user’s comments. This is where we’ll test the form data variable for the comment fields, and then copy them into another process variable to be inserted in the email.
Step 2: Service Configuration Parameters
The XQuery object is fairly simple to setup, only requiring an XQuery statement and an Output location. In the example here, I’m using a variable, emailCreditComments, to capture the comment tex which I’ll re-use in the following email workflow step.
Step 3: XQuery Statement
First, I chose a location in my process variables to act as the root of my XQuery statement. This is a parameter of the statement, rather than the XQuery configuration and acts to reduce the size of the document the XQuery statement operates on. Since I’m looking for comment fields, I set the location deep into my XFA form variable structure:
In this example, I’m using the root of the XFA data passed into the process as a form variable.
Next, as a matter of style, I set the form comments text into re-usable variables that I can test.
let $sp := //FormData/SeniorPartner/Comments/text()
Then, I constructed a logic statement that allowed for graceful regression without forcing the flow. In most functional programming languages, this style of cascading if() statement is to be avoided since it can become a real headache for maintainers. However, the nature of service components in Livecycle means that we can consider (and re-use!) pieces of functionality independently.
let $sp := //FormData/SeniorPartner/Comments/text() let $hod := //FormData/HeadOfDepartment/Comments/text() let $mgr := //FormData/Manager/Comments/text() return if (string-length($sp) = 0) then if(string-length($hod) = 0) then $mgr else $hod else $sp
As you can see the statement itself is quite simple, cascading from each if() statement, testing for validity in the order of the user hierarchy (Senior Partner, Head of Department, Manager). Validity in this case is a test for string length, such that any test that fails indicates we have a non-empty comment text node. The first test to fail gets it’s comment text returned via the else clause and inserted into the configured output location.
Lastly, since the result of the XQuery statement will be inserted into an email, rather than re-used as an XML data set, we tell the XQuery service component to omit the XML processing instruction. Other options include the ability to ‘pretty print’ the output and to re-use namespaces from the source process definition.
This example demonstrates a simple, if somewhat unusual use of the XQuery language implemented using the Avoka XQuery Service Component.