XInclude Support
(1Q18)
eXist-db comes with partial support for the XInclude standard. By default, eXist-db's XML serializer will scan all XML fragments for XInclude tags.
Introduction
The XInclude processor is implemented as a filter in between the serializer's output event stream and the receiver. If it finds an XInclude element, it will try to expand it. XInclude processing is therefore applied whenever eXist-db serializes an XML fragment,whether it's a document, the result of an XQuery or an XSLT stylesheet.
eXist-db's does not support the full XInclude standard:
-
You cannot include raw text, only XML.
-
XPointers are restricted to XPath. Additional features defined in the XPointer spec (points and locations) are not supported. However, you can use XPath functions to partly work around this and substitute XPointer's important
string-range()
function.
eXist-db expands XIncludes at serialization, which means that the query engine will see the XInclude tags before they are expanded. You therefore cannot query across XIncludes, unless you create your own code (e.g. an XQuery function) for it.
DTD entity declarations can be used for some of the things that XInclude can be used for. In general, however, XInclude is more powerful (with the exception that entity declarations are able include raw text).
The XInclude namespace (in the examples below bound to the prefix xi:
)
is:
http://www.w3.org/2001/XInclude
Including entire documents
To include an entire document, simply specify its path in the href
attribute of an <xi:include>
element.
For example, one can include a standard disclaimer, stored in the file
short-disclaimer.xml
, as follows: <xi:include
href="/db/apps/doc/data/disclaimer-short.xml"/>
If the URI in the href
attribute specifies a known scheme (like
http:
or file:
), eXist-db will try to load it as an external
resource. For example: <xi:include
href="http://localhost:8080/exist/rest/db/apps/doc/data/disclaimer-short.xml"/>
If no scheme is specified, the XInclude processor will first try to load the referenced document from the database (relative to the current collection), and if that fails, from the file system.
If the document that contains the XInclude has been constructed in an XQuery (see Including XQuery results), relative file system paths will be resolved relative to the main XQuery module source file.
Selecting parts of a resource
The xpointer
attribute of the <xi:include>
element can be
used to identify which part of the resource to include.
Selection by identifier
If the XPointer contains a simple value it is interpreted as an identifier. This
will select the element in the target document that has a matching attribute of type
identifier (ID
) (usually xml:id
).
For example, assume an XInclude target document disclaimer.xml
looks
like this:
<disclaimer>
<p xml:id="p1">
First disclaimer ...
</p>
<p xml:id="p2">
Second disclaimer ...
</p>
</disclaimer>
the following XInclude selects the element from disclaimer.xml
with an identifier
attribute with value p1
:
<xi:include href="disclaimer.xml" xpointer="p1"/>
When there are multiple instances of the same identifier attribute, only the first instance will be used.
Selecting by XPath expression
We can also use an XPath expression to select fragments. For this the
xpointer
attribute must contain an XPointer, which consists of so
called "schemes". An XPath expression can be passed through the
xpointer()
XPointer scheme.
The following expression includes the first stage direction in Shakespeare's Macbeth:
<xi:include href="/db/apps/demo/data/macbeth.xml" xpointer="xpointer(//PLAY/ACT[1]/SCENE[1]/STAGEDIR[1])"/>
Selecting by a search expression
XIncludes can also perform searches, for instance using a full-text search:
<xi:include href="/db/apps/demo/data/hamlet.xml" xpointer="xpointer(//SPEECH[ft:query(., '" slings="slings" and="and" arrows"')/LINE[1])"="arrows"')/LINE[1])""/>
The XPath expression will be applied to the entire collection if the path in
href
points to a collection and not a single document:
<xi:include href="/db/apps/demo/data"
xpointer="xpointer(//SPEECH[ft:query(., '"cursed spite"')]/LINE[1])"/>
In all cases, only the first hit is retrieved.
Namespace mappings
All namespace/prefix mappings declared in the source document are passed to the
query context. If necessary you can declare additional namespace mappings using
xmlns()
, like this:
<xi:include href="disclaimer.xml" xpointer="xpointer(//comment:comment)
xmlns(comment=http://test.org)"/>
Including XQuery results
It is possible to include the result of an XQuery script by XInclude. If the target of
an XInclude reference points to an XQuery document stored in the database (binary
resource with MIME type application/xquery
), the XInclude processor will
attempt to compile and execute this query and use its results.
For example:<xi:include href="display-collection.xq"/>
The XInclude processor declares two variables in the XQuery's static context:
-
$xinclude:current-doc
-
The name of the document which XInclude-s the query (without its collection path)
-
$xinclude:current-collection
-
the collection in which the current document resides
You can also pass additional parameters to the XQuery script: <xi:include
href="testvars.xq?var1=Hello&var2=World"/>
The parameters var1
and var2
will be available
to the XQuery as an external global variable. The XQuery needs to declare them:
declare variable $var1 external;
declare variable $var2 external;
Error Handling
An error is raised if you try to XInclude a resource which does not exist. You can
specify a fallback to avoid this. The result of the XInclude will be the content of the
<xi:fallback>
element:
<xi:include href="I-do-not exist.xml">
<xi:fallback>
<p>
The included document was not found!
</p>
</xi:fallback>
<xi:include>
A fallback element itself cannot contain XInclude elements.
Implementing XPointer string-range()
One reason why the XPointer spec has hardly seen any implementation is that it
operates with "points" and "locations" which have no equivalent in the XPath Data Model.
However, a major use case for the XPointer specification is to allow pointing at a range
of characters inside an element. Luckily, we can work around the absence of this using
the XPath functions string-join()
and substring()
.
In the following example, a range of characters in the text node of a <LINE>
element is extracted, 20 characters long, starting at the 22nd character:
<xi:include href="/db/apps/demo/data" xpointer="xpointer(
substring(string-join(//PLAY/ACT[3]/SCENE[1]/SPEECH[19]/LINE[1],''), 22, 20)
)"/>
Since only string contents is involved, these ranges can also work on text nodes belonging to different elements. In the following example, parts of succeeding lines are extracted from the SPEECH element:
<xi:include href="/db/apps/demo/data" xpointer="xpointer(
substring(string-join(//PLAY/ACT[3]/SCENE[1]/SPEECH[19], ''), 202, 24)
)"/>