Search code examples
xslttagssiblings

xslt 1.0 add (conditional) tag only if previous tag is different


I have the following source document;

<instance class-name="Deelnemer">
  <attr attr-name="achternaam">
       <value>McFly</value>
  </attr>
  <attr attr-name="geslacht">
       <value>MAN</value>
  </attr>
  <attr attr-name="geboortedatum">
       <value>1968-03-14</value>
  </attr>
  <attr attr-name="verzorgers">
       <value>{"uuid":"f5a8fecd-7ea6-499e-a8a6-c6eb20e582b8","volgnummer":1}</value>
  </attr>
  <attr attr-name="verzorgers">
       <value>{"uuid":"54748d2b-b2bf-4362-8520-4c4827a6660c","volgnummer":2}</value>
  </attr>
</instance>

I transform it using this xslt (1.0);

<xsl:for-each select="./attr[@attr-name]">
    <xsl:if test="string-length(.)"/>
    <xsl:variable name="name" select="./@attr-name"/>
    <xsl:variable name="value" select="normalize-space(./value)"/>
    <modify-attr>
        <xsl:attribute name="attr-name">
            <xsl:value-of select="$name"/>
        </xsl:attribute>
        <remove-all-values/>
        <add-value>
            <value>
                <xsl:value-of select="$value"/>
            </value>
        </add-value>
    </modify-attr>
</xsl:for-each> 

Which currently results in;

<modify-attr attr-name="geboortedatum">
  <remove-all-values/>
  <add-value>
    <value>1968-03-14</value>
  </add-value>
</modify-attr>
<modify-attr attr-name="verzorgers">
  <remove-all-values/>
  <add-value>
    <value>{"uuid":"f5a8fecd-7ea6-499e-a8a6-c6eb20e582b8","volgnummer":1}</value>
  </add-value>
</modify-attr>
<modify-attr attr-name="verzorgers">
  <remove-all-values/>
  <add-value>
    <value>{"uuid":"54748d2b-b2bf-4362-8520-4c4827a6660c","volgnummer":2}</value>
  </add-value>
</modify-attr>

But i want it to transform conditional if the previous tag (attribute) is the same as the current one.

<modify-attr attr-name="geboortedatum">
  <remove-all-values/>
  <add-value>
    <value>1968-03-14</value>
  </add-value>
</modify-attr>
<modify-attr attr-name="verzorgers">
  <remove-all-values/>
  <add-value>
    <value>{"uuid":"f5a8fecd-7ea6-499e-a8a6-c6eb20e582b8","volgnummer":1}</value>
  </add-value>
</modify-attr>
<modify-attr attr-name="verzorgers">
  <add-value>
    <value>{"uuid":"54748d2b-b2bf-4362-8520-4c4827a6660c","volgnummer":2}</value>
  </add-value>
</modify-attr>

As seen by the "verzorgers" attr-name it appears twice but i only want a <remove-all-values> with the first occurrence of this tag/attr-name.

Tried several with currently the use of "preceding-siblings" to know the previous value of "attr-name" within the loop but the syntax keeps nagging me to result in usable code. So helpful suggestions are more then welcome...


Solution

  • You could do:

    <xsl:if test="not(@attr-name = preceding-sibling::attr[1]/@attr-name)">
        <remove-all-values/>
    </xsl:if>
    

    Alternatively, you could define a key as:

    <xsl:key name="attr-by-name" match="attr" use="@attr-name" />
    

    and then do something along the lines of Muenchian grouping:

    <xsl:if test="count(. | key('attr-by-name', @attr-name)[1]) = 1">
        <remove-all-values/>
    </xsl:if>   
    

    Note that the two are not the same: you will see a difference if the groups are not contiguous.