RE: [transquery-discuss] XSLT as an XML update language

From: Evan Lenz (elenz@xyzfind.com)
Date: Fri Jan 04 2002 - 01:12:11 CET


I previously wrote:
> Finally, it might be possible to strip out the exsl:document part
> and define
> the document-replacement semantics externally on the processing
> model level.
> For example, an "update" query would "repeatedly apply this stylesheet to
> every document in the database and replace the source document with the
> result document." You might call this an "in-place transformation". This
> would have the huge advantage that we could stick with pure XSLT
> 1.0 without
> extensions. I would be interested in looking at what disadvantages there
> would be with such an approach. One is that there would be a lot
> more magic
> going on outside the XSLT (and outside the control of the query
> writer; then
> again, maybe that's a good thing).

I figured out a way to give more control back to the stylesheet writer
without using extensions at all. Recall that we need 1) something like
exsl:document, and 2) something like saxon:systemId().

We can replace exsl:document with tq:document, which is not an extension
element but a normal literal result element which has special meaning in a
TransQuery-aware processing model. In a non-TransQuery-aware processing
model, the result tree would probably just be serialized as such. However,
in a TransQuery system the tq:document element is interpreted as being an
update specification, either to add a new document or replace an existing
document, and possibly even to delete an existing document.

We can replace saxon:systemId() with XSLT's built-in generate-id() function.
How this can be done will become clear in the following modified proposal.

First, we define an element type "tq:document" which can have either an "id"
attribute or an "href" attribute but not both. (The names used are not
important; instead of "id", for example, we may want
"replaced-document-generated-id", etc.)

Here's an example with the id attribute:

<tq:document id="2134ABC">
  <myUpdatedDoc>
    This document replaces the document for whom the result of generate-id()
with its root node as context node is "2134ABC".
  </myUpdatedDoc>
</tq:document

Here's an example with the href attribute:

<tq:document href="myNewOrUpdatedDocument">
  <myDoc>
    This document is added to the repository with the URI
"myNewOrUpdatedDocument", or, if a document with that name already exists,
it replaces the existing document.
  </myDoc>
</tq:document>

A series of either of these can be used to add/replace multiple documents.
Whether they must be contained in a top-level root element is an open
question. That probably wouldn't be a bad idea for a couple of reasons: 1)
an add/replace spec could stand on its own as a well-formed document, and 2)
the root element could be used as a container for other information that one
might want to include in the result. We could say that the tq:document
elements disappear from the serialized result but everything else remains.
The effective behavior is not unlike that of exsl:document, but we're
defining it in terms of an external processing model rather than as a
requirement for XSLT processors themselves.

Finally, we make the appropriate adjustments to the imported update.xsl
module (with no extensions!):

<xsl:transform version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:tq="http://www.xmlportfolio.com/transquery">

  <xsl:param name="tq:input"/>

  <!-- Apply templates in the tq:update mode to each
       document in the TransQuery input collection -->
  <xsl:template match="/">
    <xsl:apply-templates select="$tq:input" mode="tq:update"/>
  </xsl:template>

  <!-- For each document in the TransQuery input
       collection, create a new document with the
       same generated id (effectively replacing it).
       Recursively apply templates in the tq:update
       mode to all descendants. -->
  <xsl:template match="/" mode="tq:update">
    <tq:documents>
      <tq:document id="{generate-id()}">
        <xsl:apply-templates mode="tq:update"/>
      </tq:document>
    </tq:documents>
  </xsl:template>

  <!-- The default rule for all elements,
       attributes, comments, PIs, and text
       nodes is to copy the node as is
       (and recursively apply templates to
       children of elements). -->
  <xsl:template match="@*|node()" mode="tq:update">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" mode="tq:update"/>
    </xsl:copy>
  </xsl:template>

</xsl:transform>

Update queries will work as I said previously. Here's the same example
update query again:

<xsl:transform version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:tq="http://www.xmlportfolio.com/transquery">

  <xsl:import href="update.xsl"/>

  <!-- Change the value of all attributes named
       "paid" with value "yes", to "no". -->
  <xsl:template match="@paid[.='yes']" mode="tq:update">
    <xsl:attribute name="paid">no</xsl:attribute>
  </xsl:template>

</xsl:transform>

It may also be desirable to define named templates in update.xsl for common
update directives. This could even further hide some of the complexity with
respect to namespace preservation, etc.

Evan



This archive was generated by hypermail 2.1.4 : Fri Feb 22 2002 - 11:35:58 CET