XQJ Tutorial Part IX: Creating XDM Instances



In the previous chapters, we learned how to handle XDM instances that result from query execution; iterating through sequences; and getting access to the items in the sequence. What if we want to create an XDM instance, without executing a query?

XQJ offers functionality to create both XQSequence and XQItem objects, not as a result of a query execution, but rather as standalone XDM instances. This functionality is offered through the XQDataFactory interface. An XQDataFactory can create the following types of objects:

  • XQItem
  • XQSequence
  • XQItemType
  • XQSequenceType

Every XQConnection must implement the XQDataFactory interface. In XQJ 1.0 these are the only concrete XQDataFactory implementations; future XQJ versions might introduce different mechanisms to get access to an XQDataFactory.

Creating Types

In XQJ Tutorial Part VII: XQuery Type System, we introduced the XQItemType and XQSequenceType interfaces. We also showed how these objects are used to describe the static type of a query result and external variables. How do we create such type objects in our application?

XQJ defines a dozen of XQITEMKIND_XXX constants. For each of them, there is a matching createXXXType method:

  • createAtomicType
  • createAttributeType
  • createCommentType
  • createDocumentElementType
  • createDocumentType
  • createElementType
  • creatItemType
  • createNodeType
  • createProcessingInstructionType
  • createSchemaAttributeType
  • createSchemaElementType
  • createTextType

Let's take a closer look at some of the most commonly used methods.

createAtomicType

The method createAtomicType(), creates an XQItemType object representing an XQuery atomic type. It accepts a single argument, an integer which is one of the predefined XQBASETYPE constants. The next example creates three XQItemType instances representing xs:integer, xs:string, and xs:decimal:

...
XQItemType xsinteger = xqc.createAtomicType(
XQItemType.XQBASETYPE_INTEGER);
XQItemType xsstring = xqc.createAtomicType(
XQItemType.XQBASETYPE_STRING);
XQItemType xsdecimal = xqc.createAtomicType(
XQItemType.XQBASETYPE_DECIMAL);
...

Every XQConnection is an XQDataFactory; in the example we've used our XQConnection(xqc) to create these XQItemType instances. However, the XQItemType objects are completely independent of the connection.

Where the above example shows how to create XQItemType objects representing one of the built-in atomic XML Schema types, there is a second flavor of createAtomicType() for user-defined atomic types. Assume a hatsize user-defined atomic type derived from xs:integer in the http://www.hatsize.com schema:

...
XQItemType hatsize;
hatsize = xqc.createAtomicType(
XQItemType.XQBASETYPE_INTEGER,
new QName("http://www.hatsizes.com", "hatsize"),
new URI("http://www.hatsizes.com"));
...

createElementType

Beside atomic types, element types are frequently used. In the next example we create an XQItemType representing element(person):

...
XQItemType type;
type = xqc.createElementType(
new QName("person"),
XQItemType.XQBASETYPE_ANYTYPE);
...

The first argument to createElementType() is a QName. Where in the previous example a "person" element is created in no namespace, the next example creates an element type person in the namespace http://www.example.com. The second argument can be any of the predefined types; in addition to xs:anyType, xs:untyped is frequently used:

...
XQItemType type;
type = xqc.createElementType(
new QName("person","http://www.example.com"),
XQItemType.XQBASETYPE_UNTYPED);
...

The first argument can also be null, which is assumed to be the wild card. The following code snippet shows the creation of element(*, xs:untyped):

...
XQItemType type;
type = xqc.createElementType(
null,
XQItemType.XQBASETYPE_UNTYPED);
...

createDocumentType

What about document-node() types? In the following example we create two XQItemType instances, a first representing any document, and a second representing a well-formed untyped document:

...
XQItemType type1;
XQItemType type2;
type1 = xqc.createDocumentType();
type2 = xqc.createDocumentElementType(
xqc.createElementType(
null,
XQItemType.XQBASETYPE_UNTYPED));
...

In addition to XQItemTypes, XQSequenceType objects can also be created. As explained in XQJ Tutorial Part VII: Typing, an XQSequence consists of:

  • An XQItemType
  • The cardinality required to constraint the number of items (one of the OCC_XXX constants defined on XQSequenceType)

As such creating an XQSequenceType is simple. The next example shows how to create a xs:string* sequence type:

...
XQItemType itemType;
XQSequenceType sequenceType;
itemType = xqc.createAtomicType(
XQItemType.XQBASETYPE_STRING);
sequenceType = xqc.createSequenceType(
itemType,
XQSequenceType.OCC_ZERO_OR_MORE);
...

Using Types

So, we now know how to create types. But why bother? How can we make use of all these types?

Assume an XQSequence, iterating over the items. If the item is a node retrieve it through the DOM; get atomic values as Strings. This can be accomplished using the instanceOf() method, passing in an XQItemType object:

...
XQItemType nodeType = xqc.createNodeType();
XQSequence xqs = ...
...

while (xqs.next()) {
if (xqs.instanceOf(nodeType)) {
org.w3c.dom.Node node = xqs.getNode();
...
} else {
String s = xqs.getAtomicValue();
...
}
}
...

Support for Static Types

Some XQuery implementations have support for the Static Typing Feature as defined in XQuery. This requires implementations to detect and report type errors during the static analyses phase. For expressions depending on the context item, the application must specify the static type of the context item. Why? In order to perform static typing, the implementation has to know the static type of the context item. The application has to provide the static type; failing to do so results in an error being reported during the static analysis phase.

As the static type of the context item is a static context component, the XQJ XQStaticContext interface allows it to be manipulated. The next example shows how to set the static type of the initial context item to document-node(element(*, xs:untyped)):

...
XQItemType documentType;
documentType = xqc.createDocumentElementType(
xqc.createElementType(
null,
XQItemType.XQBASETYPE_UNTYPED));
XQStaticContext xqsc = xqc.getStaticContext();
xqsc.setContextItemStaticType(documentType);
...
XQPreparedExpression xqp;
xqp = xqc.prepareExpression("//address",
xqsc);
...

Overriding Default Type Mappings

As a last use case of XQItemType, think about some of the examples described in XQJ Tutorial Part VIII: Binding External Variables. The bindXXX() methods defined on XQDynamicContext have all a third parameter which allows you to override the default Java to XQuery data type mapping.

In this example, we bind a Java Integer to the external variable; but rather than using the default mapping to xs:int, we specify to map it to a xs:short:

...
XQItemType xsshort;
xsshort = xqc.createAtomicType(XQItemType.XQBASETYPE_SHORT);
XQPreparedExpression xqp;
xqp = xqc.prepareExpression(
"declare variable $v as xs:short external; " +
"$v + 1");
xqp.bindInt(new QName("v"), 22, xsshort);
...

How to Create XDM Instances

In addition to its ability to create XQItemType and XQSequenceType instances, XQDataFactory also provides the ability to create XQItem and XQSequence instances.

If this sounds familiar to you, it's because you understand the way binding to an XQDynamicContext works, as discussed in XQJ Tutorial Part VIII: Binding External Variables. Once you know this, you're more than half the way to understanding how XQItem instances are created.

For every bindXXX() method defined on XQDynamicContext, there is a corresponding createItemFromXXX() method. Let's look at a simple example — binding a java.math.BigDecimal to an external variable $d:

...
XQExpression xqe = ...
xqe.bindObject(new QName("d"),new BigDecimal("174"), null);
...

The following examples show how to create an XQItem of type xs:decimal from the same java.math.BigDecimal:

...
XQItem xqi = xqc.createItemFromObject(new BigDecimal("174"), null);
...

XDM Instances and Connections

Note that the XQItem objects created through XQDataFactory are independent of any connection.

Suppose you execute a query returning a single item, and subsequently close the connection but still require access to the XQItem. Closing the XQConnection will invalidate the XQItem object resulting from the query execution. Because of this, XQDataFactory has an XQItem copy method, createItem(). createItem() accepts a single XQItem argument, and returns a (deep) copy of the specified item.

The following example shows how to make a query result available after closing the XQSequence or XQConnection:

...
XQConnection xqc = ...
XQExpression xqe = xqc.createExpression();
XQSequence xqs;
xqs = xqe.executeQuery("(doc('book.xml')//paragraph)[1]");
xqs.next();
XQItem xqi = xqc.createItem(xqs.getItem());
xqc.close();
// although the connection is closed, xqi is still valid.
...

Reducing Parsing of Repeated Queries

Imagine that you have an XML document that needs to be queried multiple times, but you don’t want to incur the XML parsing overhead each time it is queried. In the following example, two queries are executed, and books.xml is parsed twice:

...
XQExpression xqe = xqc.createExpression();
xqe.executeQuery("fn:doc('book.xml')//paragraph[contains(.,'XQuery')]");
xqe.executeQuery("fn:doc('book.xml')//paragraph[contains(.,'SQL')]");
...

Or suppose you receive a transient XML stream, for example in a servlet environment, and need to query the stream multiple times. Then, one way or the other, the data will need to be buffered in order to query it more than once.

How can we both ensure that an XML document is parsed only once, and, in the event the XML stream is transient, also make it 'queryable' multiple times?

Consider two XQPreparedExpression objects, xqp1 and xqp2. The following example first creates an XQItem representing the XML document; as such it will be parsed only once. Next, it is bound to two different XQPreparedExpression objects:

...
InputStream input = ...
XQItem doc = xqc.createItemFromDocument(input, null);
...
xqp1.bindItem(new QName("doc"), doc);
...
xqp2.bindItem(new QName("doc"), doc);
...

Such an approach, however, does have a couple of disadvantages, especially when working with a large documents — namely, scalability and memory consumption. For example, in the case of XQJ Tutorial Part XI: Processing Large Inputs for more information on this topic.

Finally, XQDynamicContext also allows creating XQSequence objects. The createSequence() copy operation, with its XQSequence argument, lets you return a copy of an XQSequence object. As with the XQItem example above, it allows the possibility for query results to outlive an XQConnection.

A second flavor of createSequence() accepts a java.util.Iterator, returning a sequence of items based on the objects returned by the Iterator. The objects are converted into XDM instances using the default object mapping defined in XQJ. For example, the following code snippet results in a sequence of xs:decimal instances:

...
// assume an ArrayList of BigDecimal objects
ArrayList list = ...
XQSequence s = xqc.createSequence(list.iterator());
...