Re: [xsl] Add element at the end of a variable group of elements

Subject: Re: [xsl] Add element at the end of a variable group of elements
From: "Liam R. E. Quin liam@xxxxxxxxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 3 Mar 2021 05:25:33 -0000
On Wed, 2021-03-03 at 00:01 +0000, Charles O'Connor
coconnor@xxxxxxxxxxxx wrote:
> Hi all,
> 
> Using XSLT 2.0, I'd like to add an element at the end of a group of
> elements that may vary whether they exist. 

First, some XSLT 3 approaches...

The following works fine:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="3.0" expand-text="yes"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
  xmlns:xs="http://www.w3.org/2001/XMLSchema";
  exclude-result-prefixes="#all"
  >

  <xsl:output indent="yes" />

  <!--* enable  identity transform: *-->
  <xsl:mode on-no-match="shallow-copy" />

  <xsl:template match="(foo|bar|mercury|venus)[last()]">
    <xsl:next-match/> <!--* built-in template copies the element *-->
    <moon>Now with more craters!</moon>
  </xsl:template>

</xsl:stylesheet>



So maybe that's a reason to upgrade :)

You could also define two substitution groups and use predicate
patterns to match them. You could even put (foo|bar|mergcury|venus)" in
a variable, as Wendell did, and use that in a predicate pattern, e.g.:

 <xsl:variable name="first-group" as="xs:string*"
    select='("foo", "bar", "mercury", "venus")' />

 <xsl:template match=".[
    name() = $first-group
  ][
    not(following-sibling::*[1]/name() = $first-group)
  ]">
    <xsl:next-match/> <!--* built-in template copies the element *-->
    <moon>Now with more craters!</moon>
  </xsl:template>

However, this is getting obscure, may be slow, and is not the XSLT 2
you asked for.

You may find this works:
  <xsl:template match="*[name() = ('foo', 'bar', 'mercury',
'venus')][last()]">

So here is a working XSLT 2 version:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
  xmlns:xs="http://www.w3.org/2001/XMLSchema";
  exclude-result-prefixes="#all"
  >

  <xsl:output indent="yes" />

  <xsl:template  match="*">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>

  <xsl:variable name="first-group" as="xs:string*"
    select="('foo', 'bar', 'mercury', 'venus')" />

  <xsl:template match="*[name() = $first-group][last()]" priority="10">
    <xsl:next-match/> <!--* built-in template copies the element *-->
    <moon>Now with more craters!</moon>
  </xsl:template>

</xsl:stylesheet>

Liam

-- 
Liam Quin,B https://www.delightfulcomputing.com/
Available for XML/Document/Information Architecture/XSLT/
XSL/XQuery/Web/Text Processing/A11Y training, work & consulting.
Barefoot Web-slave, antique illustrations: B http://www.fromoldbooks.org

Current Thread