15 Merging

The xsl:merge instruction allows a sorted sequence of items to be constructed by merging several input sequences. Each input sequence must have a merge key (one or more atomic values that can be computed as a function of the items in the sequence); the input sequence must either already be sorted on the value of its merge keys, or pre-sorting on these values must be requested. The merge keys for the different input sequences must be compatible in the sense that key values from an item in one sequence are always comparable with key values from an item in a different sequence.

For example, if two log files contain details of events sorted by date and time, then the xsl:merge instruction can be used to combine these into a single sequence that is also sorted by date and time.

The data written to the output sequence can be computed in an arbitrary way from the data in the input sequences, provided it follows the ordering of the input sequences.

The xsl:merge instruction can be used to merge several sequences of items that all have the same structure (more precisely, sequences whose merge keys are computed in the same way): for example, log files created by the same application running on different machines in a server farm. Alternatively, xsl:merge can be used to merge sequences that have different structure (sequences whose merge keys are computed in different ways), provided that the computed merge keys are compatible: an example might be two log files created by different applications, using different XML vocabularies, that both contain timestamped events but represent the timestamp in different ways. The xsl:merge-source element represents a set of input sequences that follow common rules, including the rules for computing the merge key. The xsl:merge operation may take any number of xsl:merge-source elements representing different rules for input sequences, and each xsl:merge-source element may describe any number (zero or more) of input sequences. The number of input sequences to the merging operation is thus fixed only at the time the xsl:merge instruction is evaluated, and may vary from one evaluation to another.

The following examples illustrate some of the possibilities. The detailed explanation of the constructs used follows later in this section.

Example: Merging All the Files in a Collection

This example takes as input a homogeneous collection of XML log files each of which contains a sorted sequence of event elements with a timestamp attribute validated as an instance of xs:dateTime. It merges the events from the input files into a single sorted output file.

<xsl:result-document href="merged-events.xml">
  <events>
    <xsl:merge>
      <xsl:merge-source for-each-source="uri-collection('log-files')"
                        select="events/event">
        <xsl:merge-key select="@timestamp"/>
      </xsl:merge-source>
      <xsl:merge-action>
        <xsl:copy-of select="current-merge-group()"/>
      </xsl:merge-action>
    </xsl:merge>
  </events>
</xsl:result-document>

The example assumes that there are several input files each of which has a structure similar to the following, in which the timestamp attribute has a typed value that is an instance of xs:dateTime:

<events>
   <event timestamp="2009-08-20T12:01:01Z">Transaction T1234 started</event>
   <event timestamp="2009-08-20T12:01:08Z">Transaction T1235 started</event>
   <event timestamp="2009-08-20T12:01:12Z">Transaction T1235 ended</event>
   <event timestamp="2009-08-20T12:01:15Z">Transaction T1234 ended</event>
</events>

The output file will have the same structure, and will contain copies of all the event elements from all of the input files, in sorted order. Note that multiple events with the same timestamp can occur either within a single file or across multiple files: the order of appearance of these events in the output file corresponds to the order of the log files within the collection (which might or might not be predictable, depending on the implementation).

 

Example: Merging Two Heterogeneous Files

This example takes as input two log files with different structure, producing a single merged output in which the entries have a common structure:

<xsl:result-document href="merged-events.xml">
  <events>
    <xsl:merge>
      <xsl:merge-source select="doc('log-file-1.xml')/events/event">
        <xsl:merge-key select="@timestamp"/>
      </xsl:merge-source>
      <xsl:merge-source select="doc('log-files-2.xml')/log/day/record">
        <xsl:merge-key select="dateTime(../@date, time)"/>
      </xsl:merge-source>
      <xsl:merge-action>
        <xsl:apply-templates select="current-merge-group()" 
                             mode="standardize-log-entry"/>
      </xsl:merge-action>
    </xsl:merge>
  </events>
</xsl:result-document>

Here the first input file has a structure similar to that shown in the previous example, while the second input has a different structure, of the form:

<log>
  <day date="2009-08-20">
    <record>
      <time>12:01:09-05:00</time>
      <message>Temperature 15.4C</message>
    </record>
    <record>
      <time>12:03:00-05:00</time>
      <message>Temperature 18.2C</message>
    </record>
  </day>
</log>

The templates in mode standardize-log-entry convert the log entries to a common output format, for example:

<xsl:template match="event" mode="standardize-log-entry" 
                            as="schema-element(event)">
  <xsl:copy-of select="." validation="preserve"/>
</xsl:template>
  
<xsl:template match="record" mode="standardize-log-entry" 
                             as="schema-element(event)">
  <event timestamp="{dateTime(../@date, time)}" xsl:validation="strict">
    <xsl:value-of select="message"/>
  </event>
</xsl:template>

Note:

The xsl:merge instruction is designed to enable streaming of data, so that there is no need to allocate memory to hold the input sequences. However, it can also be used in cases where streamed processing is not possible, for example when the input needs to be sorted.

15.1 Terminology for Merging

[Definition: A merge source definition is the definition of one kind of input to the merge operation. It selects zero or more merge input sequences, and it includes a merge key specification to define how the merge key values are computed for each such merge input sequence.] A merge source definition corresponds to an xsl:merge-source element in the stylesheet.

[Definition: A merge input sequence is an arbitrary sequenceDM30 of items which is already sorted according to the merge key specification for the corresponding merge source definition.]

[Definition: A merge key specification consists of one or more adjacent xsl:merge-key elements which together define how the merge input sequences selected by a merge source definition are sorted. Each xsl:merge-key element defines one merge key component.] For example, a merge key specification for a log file might specify two merge key components, date and time.

[Definition: A merge key component specifies one component of a merge key specification; it corresponds to a single xsl:merge-key element in the stylesheet.]

[Definition:  For each item in a merge input sequence, a value is computed for each merge key component within the merge key specification. The value computed for an item by using the Nth merge key component is referred to as the Nth merge key value of that item.]

[Definition:  The ordered collection of merge key values computed for one item in a merge input sequence (one for each merge key component within the merge key specification) is referred to as a composite merge key value.]

[Definition: A merge activation is a single evaluation of the sequence constructor contained within the xsl:merge-action element, which occurs once for each distinct composite merge key value.]

15.2 The xsl:merge Instruction

<!-- Category: instruction -->
<xsl:merge>
  <!-- Content: (xsl:merge-source+, xsl:merge-action, xsl:fallback*) -->
</xsl:merge>

The effect of the xsl:merge instruction is to produce a sorted result sequence from a number of input sequences.

The input sequences to the merge operation are defined by the xsl:merge-source child elements, as described in the next section.

The sequence constructor contained in the xsl:merge-action element is evaluated once for each distinct composite merge key value to form a partial result sequence. The result of the xsl:merge instruction is the concatenation of these partial result sequences. For example, the action might be to copy the items from all the input sequences to the result sequence without change; or it might be to select the items from one input sequence in preference to the others. In the general case, the items in the partial result sequence are produced by an arbitrary computation that has access to the items (from the various input sequences) that share the same value for the composite merge key.

The xsl:merge-source and xsl:merge-action elements are described in the following sections.

Any xsl:fallback children of the xsl:merge instruction are ignored by an XSLT 3.0 processor, but are used by an XSLT 1.0 or XSLT 2.0 processor to perform fallback processing.

Note:

An xsl:merge instruction that has no input sequences returns an empty sequence. An xsl:merge instruction with a single input sequence performs processing that is very similar in concept to xsl:for-each-group with the group-adjacent attribute, except that it requires the input to be sorted on the grouping key.

15.3 Selecting the Sequences to be Merged

<xsl:merge-source
  name? = ncname
  for-each-item? = expression
  for-each-source? = expression
  select = expression
  streamable? = boolean
  use-accumulators? = tokens
  sort-before-merge? = boolean
  validation? = "strict" | "lax" | "preserve" | "strip"
  type? = eqname >
  <!-- Content: xsl:merge-key+ -->
</xsl:merge-source>

Each xsl:merge-source element defines one or more merge input sequences.

The name attribute provides a means of distinguishing items from different merge sources within the xsl:merge-action instructions. If the name attribute is present on an xsl:merge-source element, then it must not be equal to the name attribute of any sibling xsl:merge-source element. If the name attribute is absent, then an implementation-dependent name, different from all explicitly specified names, is allocated to the merge source.

[ERR XTSE3195] If the for-each-item is present then the for-each-source, use-accumulators, and streamable attributes must both be absent. If the use-accumulators attribute is present then the for-each-source attribute must be present. If the for-each-source attribute is present then the for-each-item attribute must be absent.

The use-accumulators attribute defines the set of accumulators that are applicable to the streamed document, as explained in 18.2.2 Applicability of Accumulators.

If neither of for-each-item and for-each-source is present, the xsl:merge-source element defines a single merge input sequence. This sequence is the result of evaluating the expression in the select attribute. This is evaluated using the dynamic context of the containing xsl:merge instruction. This sequence will be merged with the sequences defined by other xsl:merge-source elements, if present.

When the for-each-item attribute is present, the xsl:merge-source element defines a collection of merge input sequences. The selection of items in these input sequences is a two-stage process: the for-each-item attribute of the xsl:merge-source element is an expression that selects a sequence of anchor items, and for each anchor item, the select attribute is evaluated to select the items that make up one merge input sequence. The for-each-item expression is evaluated with the dynamic context of the containing xsl:merge instruction, while the select attribute is evaluated with the focus for the evaluation as follows:

When the for-each-source attribute is present, its value must be an expression that returns a sequence of URIs. The expression is evaluated with the same dynamic context as the containing xsl:merge instruction. The expected type of the expression is xs:string*, and the actual result of the expression is converted to this type using the function conversion rules. Each of these URIs is used to obtain a document node. Each must be a valid URI reference. If it is an absolute URI reference, it is used as is; if it is a relative URI reference, it is made absolute by resolving it against the base URI of the xsl:merge-source element. The process of obtaining a document node given a URI is the same as for the docFO30 function, and may trigger the same error conditions. However, unlike the docFO30 function, the xsl:merge instruction offers no guarantee that the resulting document will be stable (that is, that multiple calls specifying the same URI will return the same document). The resulting document nodes act as the anchor items. These anchor items are then used in the same way as a sequence of anchor items selected directly using the for-each-item attribute: in particular, the focus is determined in the same way.

Note:

Examples of expressions that return a sequence of URIs are:

  • for-each-source="'inputA.xml', 'inputB.xml'"

  • for-each-source="(1 to $N) ! ('input' || $N || '.xml')"

  • for-each-source="uri-collection('input/dir/')

Relative URIs are resolved relative to the base URI of the xsl:merge-source element.

The attributes validation and type are used to control schema validation of documents read by virtue of their appearance in the result of the for-each-source expression. These attributes are mutually exclusive [see ERR XTSE1505]. The rules are the same as for an xsl:source-document instruction specifying streamable="yes". If the for-each-source attribute is absent, then the validation and type attributes must both be absent.

If the sort-before-merge attribute is absent or has the value no, then each input sequence must already be in the correct order for merging (a dynamic error occurs if it is not). If the attribute is present with the value yes, then each input sequence will first be sorted to ensure that it is in the correct order.

Example: Merging Several Documents with the Same Structure

The following xsl:merge-source element selects two anchor items (the root nodes of two documents), and for each of these it selects an input sequence consisting of selected event elements within the relevant document.

<xsl:merge-source for-each-source="'log-A.xml', 'log-B.xml'"
                  streamable="yes"
                  select="events/event">
   <xsl:merge-key select="@timestamp" order="ascending"/>
</xsl:merge-source>

This example can be extended to merge any number of input documents with the same structure:

<xsl:merge-source for-each-source="uri-collection('log-collection')"
                  streamable="yes"
                  select="events/event">
   <xsl:merge-key select="@time" order="ascending"/>
</xsl:merge-source>

In both the above examples the anchor items are document nodes, and the items in the input sequence are elements within the document that is rooted at this node. This is a common usage pattern, but by no means the only way in which the construct can be used.

The number of anchor items selected by an xsl:merge-source element, and therefore the number of input sequences, is variable, but the input sequences selected by one xsl:merge-source element must all use the same expressions to select the items in the input sequence and to compute their merge keys. If different expressions are needed for different input sequences, then multiple xsl:merge-source elements can be used.

Example: Merging Two Documents with Different Structure

The following code merges two log files having different internal structure:

<xsl:merge-source for-each-source="'event-log.xml'" 
                  streamable="yes" select="/*/event">
  <xsl:merge-key select="@timestamp"/>
</xsl:merge-source>
<xsl:merge-source for-each-source="'error-log.xml'" 
                  streamable="yes" select="/*/error">
  <xsl:merge-key select="dateTime(@date, @time)"/>
</xsl:merge-source>

Although the merge keys are computed in different ways for the two input sequences, the keys must be compatible across the two sequences: in this case they are both atomic values of type xs:dateTime.

In the common case where there is only one input sequence of a particular kind, the for-each-item attribute of xsl:merge-source may be omitted; the select expression is then evaluated relative to the focus of the xsl:merge instruction itself.

Example: Sorting before Merging

Where one or more of the inputs to the merging process is not pre-sorted, a sort can be requested using the sort-before-merge attribute. For example:

<xsl:merge-source select="doc('event-log.xml')/*/event">
  <xsl:merge-key select="@timestamp"/>
</xsl:merge-source>
<xsl:merge-source select="doc('error-log.xml')//error" 
                  sort-before-merge="yes">
  <xsl:merge-key select="dateTime(current-date(), @time)"/>
</xsl:merge-source>

[ERR XTSE3190] It is a static error if two sibling xsl:merge-source elements have the same name.

15.4 Streamable Merging

Any input to a merging operation, provided it is selected by means of the xsl:merge-source element with a for-each-source attribute, may be designated as streamable by including the attribute streamable="yes" on the xsl:merge-source element.

When streamable="yes" is specified on an xsl:merge-source element, then (whether or not streamed processing is actually used, and whether or not the processor supports streaming) the expression appearing in the select attribute is implicitly used as the argument of a call on the snapshot function, which means that merge keys for each selected node are computed with reference to this snapshot, and the current-merge-group function, when used within the xsl:merge-action sequence constructor, delivers snapshots of the selected nodes.

Note:

There are therefore no constraints on the navigation that may be performed in computing the merge key, or in the course of evaluating the xsl:merge-action body. An attempt to navigate outside the portion of the source document delivered by the snapshot function will typically not cause an error, but will return empty results.

There is no rule to prevent the select expression returning atomic values, or grounded nodes from a different source document, or newly constructed nodes, but they are still processed using the snapshot function.

Because the snapshot copies accumulator values as described in 18.2.10 Copying Accumulator Values, the functions accumulator-before and accumulator-after may be used to gain access to information that is not directly available in the nodes that are present within each snapshot (for example, information in a header section of the merge input document).

An xsl:merge-source element is guaranteed-streamable if it satisfies all the following conditions:

  1. The xsl:merge-source element has the attribute value streamable="yes";

  2. The for-each-source attribute is present on that xsl:merge-source element;

  3. The expression in the select attribute of that xsl:merge-source element, assessed with a context posture of striding and a context item type of U{document-node()}, has striding or grounded posture and motionless or consuming sweep;

  4. The sort-before-merge attribute of that xsl:merge-source element is either absent or takes its default value of no.

Specifying streamable="yes" on an xsl:merge-source element declares an intent that the xsl:merge instruction should be streamable with respect to that particular source, either because it is guaranteed-streamable, or because it takes advantage of streamability extensions offered by a particular processor. The consequences of declaring the instruction to be streamable when it is not in fact guaranteed streamable depend on the conformance level of the processor, and are explained in 19.10 Streamability Guarantees.

Example: Streamed Merging

The following example merges two log files, processing each of them using streaming.

<events>
   <xsl:merge>
      <xsl:merge-source for-each-source="'log-file-1.xml'" 
                        select="/events/event" 
                        streamable="yes">
         <xsl:merge-key select="@timestamp"/>
      </xsl:merge-source>
      <xsl:merge-source for-each-source="'log-files-2.xml'" 
                        select="/log/day/record" 
                        streamable="yes">
         <xsl:merge-key select="dateTime(../@date, time)"/>
      </xsl:merge-source>
      <xsl:merge-action>
         <events time="{current-merge-key()}">
            <xsl:copy-of select="current-merge-group()"/>
         </events>   
      </xsl:merge-action>
   </xsl:merge>
</events>

Note that the merge key for the second merge source includes data from a child element of the selected element and also from an attribute of the parent element. This works because of the merge key is evaluated on the result of implicitly applying the snapshot function.

Example: Merging XML and non-XML Data

The following example merges two log files, one in text format and one in XML format.

<events>
   <xsl:merge>
      <xsl:merge-source name="fax" 
                        select="unparsed-text-lines('fax-log.txt')">
         <xsl:merge-key select="xs:dateTime(substring-before(., ' '))"/>
      </xsl:merge-source>
      <xsl:merge-source name="mail"
                        for-each-source="'mail-log.xml'" 
                        select="/log/day/message" 
                        streamable="yes">
         <xsl:merge-key select="dateTime(../@date, @time)"/>
      </xsl:merge-source>
      <xsl:merge-action>
         <messages at="{current-merge-key()}">
            <xsl:where-populated>
               <fax>
                  <xsl:for-each select="current-merge-group('fax')">
                     <message xsl:expand-text="true">{
                        substring-after(., ' ')
                     }</message>
                  </xsl:for-each>   
               </fax>
               <mail>
                  <xsl:sequence select="current-merge-group('mail')/*"/>
               </mail>
            </xsl:where-populated>   
         </messages>   
      </xsl:merge-action>
   </xsl:merge>
</events>

15.5 Defining the Merge Keys

The keys on which the input sequences are sorted are referred to as merge keys. If the attribute sort-before-merge has the value yes, the input sequences will be sorted into the correct sequence before the merge operation takes place (alternatively, the processor may use an algorithm that has the same effect as sorting followed by merging). If the attribute is absent or has the value no, then the input sequences must already be in the correct order.

The merge key for each type of input sequence (that is, for each xsl:merge-source element) is defined by a sequence of xsl:merge-key element children of the xsl:merge-source element. Each xsl:merge-key element defines one merge key component. The syntax and semantics of an xsl:merge-key element are closely based on the rules for the xsl:sort element (the only exception being the absence of the stable attribute); the difference is that xsl:merge-key elements do not cause a sort to take place, they merely declare the existing sort order of the input sequence.

<xsl:merge-key
  select? = expression
  lang? = { language }
  order? = { "ascending" | "descending" }
  collation? = { uri }
  case-order? = { "upper-first" | "lower-first" }
  data-type? = { "text" | "number" | eqname } >
  <!-- Content: sequence-constructor -->
</xsl:merge-key>

The select attribute and the contained sequence constructor are mutually exclusive:

[ERR XTSE3200] It is a static error if an xsl:merge-key element with a select attribute has non-empty content.

The value of Nth merge key value of an item J in a merge input sequence S is the result of the expression in the select attribute of the Nth xsl:merge-key child of the corresponding xsl:merge-source element, or in the absence of the select attribute, the result of the contained sequence constructor. This is evaluated with a singleton focus based on J, or, if streamable=yes is specified on the xsl:merge-source, a singleton focus based on a snapshot of J (see 15.4 Streamable Merging).

Note:

This means that position() and last() return 1 (one). This differs from the way xsl:sort keys are evaluated, where position() is the position in the unsorted sequence, and last() is the size of the unsorted sequence.

The effect of the xsl:merge-key elements is defined in terms of the rules for an equivalent sequence of xsl:sort elements: if the rules for sorting (see 13.1.1 The Sorting Process) with stable="yes" would place an item A before an item B in the sorted sequence produced by the sorting process, then A must precede B in the input sequence to the merging process.

The merge keys of the various input sequences to a merge operation must be compatible with each other, since the merge operation will decide the ordering of the result sequence by comparing merge key values across input sequences. This means that across all the xsl:merge-source children of an xsl:merge instruction:

If any of the attributes lang, order, collation, case-order, or data-type are attribute value templates, then their effective values are evaluated using the focus of the containing xsl:merge instruction.

[ERR XTSE2200] It is a static error if the number of xsl:merge-key children of a xsl:merge-source element is not equal to the number of xsl:merge-key children of another xsl:merge-source child of the same xsl:merge instruction.

[ERR XTDE2210] It is a dynamic error if there are two xsl:merge-key elements that occupy corresponding positions among the xsl:merge-key children of two different xsl:merge-source elements and that have differing effective values for any of the attributes lang, order, collation, case-order, or data-type. Values are considered to differ if the attribute is present on one element and not on the other, or if it is present on both elements with effective values that are not equal to each other. In the case of the collation attribute, the values are compared as absolute URIs after resolving against the base URI. The error may be reported statically if it is detected statically.

[ERR XTDE2220] It is a dynamic error if any input sequence to an xsl:merge instruction contains two items that are not correctly sorted according to the merge key values defined on the xsl:merge-key children of the corresponding xsl:merge-source element, when compared using the collation rules defined by the attributes of the corresponding xsl:merge-key children of the xsl:merge instruction, unless the attribute sort-before-merge is present with the value yes.

[ERR XTTE2230] It is a type error if some item selected by a particular merge key in one input sequence is not comparable using the XPath le operator with some item selected by the corresponding sort key in another input sequence.

15.6 The Current Merge Group and Key

During processing of an xsl:merge instruction, two additional values are available within the dynamic context:

These values are made available through the functions current-merge-group and current-merge-key.

The current merge group and current merge key are available within the sequence constructor contained by an xsl:merge-action element. The values are initially absent during the evaluation of global variables and stylesheet parameters, during the evaluation of the use attribute or contained sequence constructor of xsl:key, and during the evaluation of the initial-value attribute of xsl:accumulator and the select attribute of contained sequence constructor of xsl:accumulator-rule. All invocation constructs set the current merge group and current merge key to absent.

Note:

Taken together, these rules mean that any invocation of current-merge-group or current-merge-key that is not lexically scoped by an xsl:merge-action element will raise a dynamic error.

When an inner xsl:merge instruction is lexically nested within the xsl:merge-action element of an outer xsl:merge instruction, any use of current-merge-group or current-merge-key that appears within the xsl:merge-action of the inner xsl:merge instruction is a reference to the current merge group or current merge key of the inner xsl:merge instruction, while any such reference that appears within the outer xsl:merge-action element, but not within the inner xsl:merge-action, is a reference to the current merge group or current merge key of the outer xsl:merge instruction. This means, for example, that a reference to the current merge group of the outer xsl:merge can appear in the select attribute of an xsl:merge-source child of the inner xsl:merge.

On completion of the evaluation of the xsl:merge-action sequence constructor, the current merge group and current merge key revert to their previous values.

15.6.1 fn:current-merge-group

Summary

Returns the group of items currently being processed by an xsl:merge instruction.

Signatures
fn:current-merge-group() as item()*
fn:current-merge-group($source as xs:string) as item()*
Properties

This function is deterministicFO30, context-dependentFO30, and focus-independentFO30.

Rules

The current merge group is bound during evaluation of the xsl:merge-action child of an xsl:merge instruction. If no xsl:merge-action is being evaluated, then the current merge group is absent, in which case the function raises a dynamic error (see below).

The current merge group (if not absent) is a map. It contains the set of items, from all merge inputs, that share a common value for the merge key. This is structured as a map so that the items from each merge source can be identified. The key in the map is the value of the name attribute of the corresponding xsl:merge-source element (or an invented name, in its absence), and the associated value is the set of items contributed by that merge group.

The map itself is not made visible, but this function returns values derived from the map. Specifically, if the map is denoted by $G:

  • The single-argument form of this function returns the value of the expression if (map:contains($source)) then $G($source) else error(). Informally, if there is an xsl:merge-source element whose name attribute matches $source, the function returns the items in the current merge group that are contributed by this merge source; otherwise it raises a dynamic error (see below).

  • The zero-argument form of the function returns the value of the expression sort(map:keys($G))!$G(.), where the sort() function sorts the names of xsl:merge-source elements into the document order of the xsl:merge-source elements in the stylesheet. Informally, it returns all the items in the current merge group regardless of which merge source they derive from.

Within the current merge group, the ordering of items from the input sequences is as follows, in major-to-minor order:

  • Items are first ordered by the xsl:merge-source element that defined the input sequence from which the item was taken; items from xsl:merge-source A precede items from xsl:merge-source B if A precedes B in document order within the stylesheet.

  • Items from different input sequences selected by the same xsl:merge-source element are then ordered based on the order of the anchor items in the sequence selected by evaluating the select attribute of the xsl:merge-source element.

  • Finally, duplicate items from the same input sequence retain their order from the input sequence.

Duplicates are not eliminated: for example, if the same node is selected in more than one input sequence, it may appear twice in the current merge group.

Error Conditions

[ERR XTSE3470] It is a static error if the current-merge-group function is used within a pattern.

[ERR XTDE3480] It is a dynamic error if the current-merge-group function is used when the current merge group is absent. The error may be reported statically if it can be detected statically.

[ERR XTDE3490] It is a dynamic error if the $source argument of the current-merge-group function does not match the name attribute of any xsl:merge-source element for the current merge operation. The error may be reported statically if it can be detected statically.

Notes

Because the current merge group is cleared by function calls and template calls, the current-merge-group function only has useful effect when the call appears as a descendant of an xsl:merge-action element.

If an xsl:merge-source element has no name attribute, then it is not possible to discover the items in the current merge group that derive specifically from that source, but these items will still be present in the current merge group, and will be included in the result when the function is called with no arguments.

Like other XSLT extensions to the dynamic evaluation context, the current merge group is not retained as part of the closure of a function value. This means that the expression current-merge-group#0 is valid and returns a function value, but any invocation of this function will fail with a dynamic error [see ERR XTDE3480].

15.6.2 fn:current-merge-key

Summary

Returns the merge key of the merge group currently being processed using the xsl:merge instruction.

Signature
fn:current-merge-key() as xs:anyAtomicType*
Properties

This function is deterministicFO30, context-dependentFO30, and focus-independentFO30.

Rules

The evaluation context for XPath expressions includes a component called the current merge key, which is a sequence of atomic values. The current merge key is the composite merge key value shared in common by all the items within the current merge group.

The function current-merge-key returns the current merge key.

While the xsl:merge-action child of an xsl:merge instruction is being evaluated, the current merge key will be a single atomic value if there is a single merge key, or a sequence of atomic values if there are multiple merge keys.

At other times, the current merge key will be absent.

The merge keys of all items in a group are not necessarily identical. For example, one might be an xs:float while another is a numerically equal xs:decimal. The current-merge-key function returns the merge key of the first item in the group, after atomization and casting of xs:untypedAtomic values to xs:string.

Error Conditions

[ERR XTSE3500] It is a static error if the current-merge-key function is used within a pattern.

[ERR XTDE3510] It is a dynamic error if the current-merge-key function is used when the current merge key is absent, or when it is invoked in the course of evaluating a pattern. The error may be reported statically if it can be detected statically.

Notes

Like other XSLT extensions to the dynamic evaluation context, the current merge key is not retained as part of the closure of a function value. This means that the expression current-merge-key#0 is valid and returns a function value, but any invocation of this function will fail with a dynamic error [see ERR XTDE3510].

15.7 The xsl:merge-action Element

The xsl:merge-action child of an xsl:merge instruction defines the processing to be applied for each distinct composite merge key value found in the input sequences to the xsl:merge instruction.

<xsl:merge-action>
  <!-- Content: sequence-constructor -->
</xsl:merge-action>

The merge key values for each item in an input sequence are calculated based on the corresponding xsl:merge-key elements, in the same way as sort key values are calculated using a sequence of xsl:sort elements (see 13.1.1 The Sorting Process). If several items from the same or from different input sequences have the same values for all their merge keys (comparing pairwise), then they are considered to form a group. The sequence constructor contained in the xsl:merge-action element is evaluated once for each such group of items, and the result of the xsl:merge instruction is the concatenation of the results obtained by processing each group in turn.

The groups are processed one by one, based on the values of the merge keys for the group. If group G has a set of merge key values M, while group H has a set of merge key values N, then in the result of the xsl:merge instruction, the result of processing group G will precede the result of processing H if and only if M precedes N in the sort order defined by the lang, order, collation, case-order, and data-type attributes of the merge key definitions.

Generally, two sets of merge key values are distinct if any corresponding items in the two sets of values do not compare equal under the rules for the XPath eq operator, under the collating rules for the corresponding merge key definition. In rare cases, when considering more than two sets of merge key values, ambiguities may arise because of the non-transitivity of the eq operator when applied across different numeric types. In this situation, the partitioning of items into sets having distinct key values is handled in the same way as for xsl:for-each-group (see 14.5 Non-Transitivity), and is to some extent implementation-dependent.

The focus for evaluation of the sequence constructor contained in the xsl:merge-action element is as follows:

Example: Selective Processing of Merge Inputs

Consider a situation where there are two merge sources, named "master" and "update"; the master source identifies a single merge input file (the master file), while the update source identifies a set of N update files, perhaps one for each day of the week. The required logic is that if a merge key is present only in the master file, then the corresponding item should be copied to the output; if it is present in a single update file then that item replaces the corresponding item from the master file; if it is present in several update files, then an error is raised. This can be achieved as follows:

<xsl:merge>
  <xsl:merge-source name="master" 
                    for-each-source="'master.xml'"
                    streamable="yes"
                    select="/events/event">
      <xsl:merge-key select="@key"/>
  </xsl:merge-source>
  <xsl:merge-source name="updates" 
                    for-each-source="uri-collection('updates')"
                    streamable="yes"
                    select="/events/event-change">
      <xsl:merge-key select="@affected-key"/>
  </xsl:merge-source>
  <xsl:merge-action>
    <xsl:choose>
      <xsl:when test="empty(current-merge-group('master'))">
        <xsl:message>
           Error: update is present with no matching master record!
        </xsl:message>
      </xsl:when>
      <xsl:when test="empty(current-merge-group('updates'))">
        <xsl:copy-of select="current-merge-group('master')"/>
      </xsl:when>
      <xsl:when test="count(current-merge-group('updates')) = 1">
        <xsl:copy-of select="current-merge-group('updates')"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message>
           Conflict: multiple updates for the same master record!
        </xsl:message>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:merge-action>
</xsl:merge>
            

Some words of explanation:

  • Error messages are produced if there is an update element whose key does not correspond to any element in the master source, or if there is more than one update element corresponding to the same master element.

  • In the absence of errors, if there is a single update element then it is copied to the output; if there is none, then the master element is copied.

15.8 Examples of xsl:merge

Previous sections introduced examples designed to illustrate some specific features of the xsl:merge instruction. This section provides some further examples to illustrate different ways in which the instruction can be used.

Example: Applying Transactions to a Master File

This example applies transactions from a transaction file to a master file. Records in the master file for which there is no corresponding transaction are copied unchanged. The transaction file contains instructions to delete, replace, or insert records identified by an ID value. The master file is known to be sorted on the ID value; the transaction file is unsorted.

Master file document structure:

<data>
  <record ID="A0001">...</record>
  <record ID="A0002">...</record>
  <record ID="A0003">...</record>
</data>

Transaction file document structure:

<transactions>
  <update record="A0004" action="insert">...</update>
  <update record="A0002" action="delete"/>
  <update record="A0003" action="replace">...</update>
</transactions>

Solution:

<xsl:merge>
  <xsl:merge-source name="master" 
                    select="doc('master.xml')/data/record">
      <xsl:merge-key select="@ID"/>
  </xsl:merge-source>
  <xsl:merge-source name="updates"
                    sort-before-merge="yes"
                    select="doc('transactions.xml')/transactions/update">     
      <xsl:merge-key select="@record"/>
  </xsl:merge-source>
  <xsl:merge-action>
    <xsl:choose>
      <xsl:when test="empty(current-merge-group('updates'))">
        <xsl:copy-of select="current-merge-group('master')"/>
      </xsl:when>
      <xsl:when test="current-merge-group('updates')/@action=('insert', 'replace')">
        <record ID="{current-merge-key()}">
          <xsl:copy-of select="current-merge-group('updates')/*"/>
        </record>
      </xsl:when>
      <xsl:when test="current-merge-group('updates')/@action='delete'"/>
    </xsl:choose>
  </xsl:merge-action>
  </xsl:merge>

 

Example: Merging Two Sequences of Numbers

The xsl:merge instruction can be used to determine the union, intersection, or difference of two sequences of numbers (or other atomic values). This code gives the union:

<xsl:merge>
  <xsl:merge-source select="1 to 30">
      <xsl:merge-key select="."/>
  </xsl:merge-source>
  <xsl:merge-source select="20 to 40">
      <xsl:merge-key select="."/>
  </xsl:merge-source>
  <xsl:merge-action>
    <xsl:sequence select="current-merge-key()"/>
  </xsl:merge-action>
</xsl:merge>
               

While this gives the intersection:

<xsl:merge>
  <xsl:merge-source select="1 to 30">
      <xsl:merge-key select="."/>
  </xsl:merge-source>
  <xsl:merge-source select="20 to 40">
      <xsl:merge-key select="."/>
  </xsl:merge-source>
  <xsl:merge-action>
    <xsl:if test="count(current-merge-group()) eq 2">
      <xsl:sequence select="current-merge-key()"/>
    </xsl:if>
  </xsl:merge-action>
</xsl:merge>