Re: [xsl] Generate separate elements, not just attribute values

Subject: Re: [xsl] Generate separate elements, not just attribute values
From: "Michael Kay mike@xxxxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Thu, 23 Mar 2017 19:59:25 -0000
> On 23 Mar 2017, at 18:23, Charles O'Connor coconnor@xxxxxxxxxxxx
<xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:
>
> Hi all,
>
> Extreme novice here, so I appreciate your help.
>
> Using XSLT 2.0 (explained later) and Saxon9 HE.
>
> I have JATS 1.1 (archiving) input:
>
> . . .
>
> <contrib-group>
> 	<contrib contrib-type="author">
> 		<name>
> 			<surname>Franzke</surname>
> 			<given-names>Christian L. E.</given-names>
> 		</name>
> 	</contrib>
> 	<aff id="aff2">Meteorological Institute and Center for Earth System
Research and Sustainability (CEN), <institution>University of
Hamburg</institution>, <country>Germany</country></aff>
> 	<aff id="aff3">Department of Cheddar, <institution>University of Curds and
Whey</institution>, <country>Land of Cheese</country></aff>
> </contrib-group>
>
> . . .
>
> To make it easier for our engineers to recognize relationships between
<contrib>s and <aff>s in cases where they are related through nesting in a
<contrib-group>, not <xref>s, I wrote a small .xsl:
>
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
>    xmlns:xs="http://www.w3.org/2001/XMLSchema"; exclude-result-prefixes="xs"
version="2.0">
>    <xsl:template match="@* | node()">
>        <xsl:copy>
>            <xsl:apply-templates select="@* | node()"/>
>        </xsl:copy>
>    </xsl:template>
>    <xsl:template match="//contrib[not(xref/@ref-type='aff')]">
>        <xsl:copy>
>            <xsl:apply-templates select="@* | node()"/>
>            <xsl:element name="xref">
>                <xsl:attribute name="ref-type">aff</xsl:attribute>
>                <xsl:attribute name="rid">
>                    <xsl:value-of select="parent::contrib-group/aff/@id"/>
>                </xsl:attribute>
>            </xsl:element>
>        </xsl:copy>
>    </xsl:template>
> </xsl:stylesheet>

You could be a lot less verbose:

>  <xsl:template match="//contrib[not(xref/@ref-type='aff')]">
>        <xsl:copy>
>            <xsl:apply-templates select="@* | node()"/>
>            <xref ref-type="aff" id="{../aff/@id}"/>
>        </xsl:copy>
>    </xsl:template>


>
>
> But in cases of more than one <aff> in a <contrib-group>, instead of
>
> 	<xref ref-type="aff" rid="aff2 aff3"/>
>
> it turns out the engineers really want
>
> 	<xref ref-type="aff" rid="aff2"/><xref ref-type="aff" rid="aff3"/>
>
> Do I need to use a "for-each" to do this?

Yes, you do:

<xsl:for-each select="../aff">
   <xref ref-type="aff" id="{@id}"/>
</xsl:for-each>

> Incidental question: I have the version as 2.0 because, well, that was the
version on the identity template I copied from wherever. I didn't see any
reason for it to be 2.0, however, and 1.0 would be easier because you can run
1.0 in .NET without additional software. But, when I changed the version to
1.0, it ran fine but only gave the first @rid value, i.e.,
>
> <xref ref-type="aff" rid="aff2"/>
>
> Why?
>
Because in XSLT 1.0, xsl:value-of (and many other operations), if given a set
of nodes as input, ignores all but the first node in the set.

Michael Kay

Current Thread