Easy XPath retrieval in Java
Many experienced Mendix Developers use Java to express low-level, complex algorithms. Retrieving data from the database using XPath is often part of this process. The verbosity of Java in combination with its somewhat limited type checker (see this blogpost) makes this quite a tedious process. However, the latest community commons release (2.3) includes an XPath utility class which significantly eases retrieving data from the database. This blog post demonstrates how to use this XPath class to significantly simplify your java code.
Let’s, for example, take the following domain model and method:
As an example let’s try to implement the following java action (the case is somewhat artificial to keep it as short as possible):
If you would create a microflow, the XPath used to solve the problem would look like this:
Some naive implementations
A first, naive java implementation would look like this:
However, using raw strings in XPath is generally recognized as a bad practice; if the domain model changes you would like to get compile errors, but you will not get those when using strings. Fortunately this can easily be avoided by using format strings and the proxy classes generated by the Mendix Business Modeler. As soon as the domain model changes, the proxy classes are regenerated, and the identifiers used in this code will become unresolvable (and thus result in compiler errors).
Unfortunately this approach still has its limitations:
- The approach is very sensitive to database injection attacks, take for example the search query (mind the single quote): “
' or true() = true()“
- If no category is provided, a null pointer exception will be thrown, instead of yielding no results.
- Setting up a proper format string is an error prone process, especially when changing the query in a later stadium. If the query consists of many identifiers, it becomes a tedious process to match a formatter with its substitution, making the code hardly readable.
To avoid the first two issues, one has to introduce additional null checks (
if (category == null) return "";) and sanitize the input (
StringEscapeUtils.escapeXml(searchQuery)). Both exceptional cases are easily forgotten.
The XPath generator class
The XPath class provided by the community commons module provides an easier approach to tackle this problem. The class provides a fluent interface to writing XPath. Using generics, type inference and class reflection, the class allows you to write XPath queries which are structurally equal to the XPath expressions generated by the Business Modeler.
XPath expressions are constructed using the
XPath.Create(context, proxyclass) method. This class instantiates a fluent XPath interface and binds it to a specific Entity type. Or to just dive into the code:
Using the XPath class, our XPath expression is now structurally equivalent to the original XPath expression as defined in the Business Modeler. Furthermore a lot of code has been eliminated:
- No proxy instances or lists need to be instantiated (
List<IMendixObject>), this saves a lot of boilerplate null checks and unnecessary variables, significantly reducing the amount of code.
- Values are converted automatically. For example a java boolean tranlates nicely into XPath
- No null checks are needed, since we can pass both proxy classes or IMendixObject objects directly into the XPath. Null checks are performed by the library and translate correctly into XPath
- No data sanitizing is needed, string values are always sanitized by the library.
- Common functions like
countare directly available as methods of the fluent interface.
- XPath constant values are easily introduced in the query by using for example
It would be to superfluous to explain the full fluent API here, but one can easily dive into (and extend) the class at
javasource/communitycommons/XPath.java after importing the module. Feel free to post any improvements or suggestions here!