0

Can I add an element based on previous data? I would like to add that to an existing complex XSLT ruleset. That's why I wouldn't like to start with another program.

A simplified version of the problem:

<featurecollection>
  <feature> 
    <id>1235</id>    <!-- same id --> 
    <time>100</time> <!-- older ----> 
  </feature>
  <feature>
    <id>1235</id>    <!-- same id --> 
    <time>101</time> <!-- newer ----> 
  </feature>
  
  <feature>
    <id>1236</id>    <!-- unique id -> 
    <time>102</time> <!-- is new ----> 
  </feature>
</featurecollection>

I would like to add an extra element based upon the time of an existing id.

<featurecollection>
  <feature> 
    <id>1235</id>           <!-- same id --> 
    <time>100</time>        <!-- older ----> 
    <sequence>first</time>
  </feature>
  <feature>
    <id>1235</id>           <!-- same id --> 
    <time>101</time>        <!-- newer ----> 
    <sequence>second</time>
  </feature>
  
  <feature>
    <id>1236</id>            <!-- unique id -> 
    <time>102</time>         <!-- is new ----> 
    <sequence>second</time>
  </feature>
</featurecollection>

Any id will be used either once or twice. With similar id's the oldest is always before the newest. The id field is always before the <time> field.

How can I add that <sequence> element? I could use a Java extension, but then I am operating within another element. Via the Saxon lib I can work with XSLT 2.0.

ADDED: A more elaborate version of the problem is the following XML:

<featurecollection>
  <feature>
    <object>
      <identification>
        <id>1</id>
      </identification>
      <time>20230804120304</time>
      <!-- insert here: <sequence>first</sequence> -->
    </object>
    <object>
      <identification>
        <id>1</id>
      </identification>
      <time>20230804120305</time>
      <!-- insert here: <sequence>second</sequence> -->
    </object>
  </feature>
  <feature>
    <object>
      <identification>
        <id>2</id>
      </identification>
      <time>20230804120304</time>
      <!-- insert here: <sequence>second</sequence> -->
    </object>
  </feature>
</featurecollection>

UPDATE: looking back, thank you all very much for the great answers!

tm1701
  • 7,307
  • 17
  • 79
  • 168
  • 1
    it would help if you showed at least a snippet of your current XSLT, such as the template(s) for processing the `feature`, `id`, and `time` elements. It's hard to suggest how to fit something into your existing XSLT if we don't know anything about it. – Mads Hansen Aug 03 '23 at 19:29
  • You are right. Thank you. The original XSLT sheet is very long and difficult to understand. I tried to extract the main thought from it in the XML above. *THANK YOU* for improving the question! The suggested answer below helped me in the right direction. +1 for the comment. – tm1701 Aug 04 '23 at 10:05
  • I added a more realistic version of the XML. – tm1701 Aug 04 '23 at 10:15
  • 1
    In all your questions regarding XSLT, please state which version of XSLT your processor supports. – michael.hor257k Aug 04 '23 at 12:07

2 Answers2

1

It's not clear to me what the condition is for adding the additional element, so I've made a working hypothesis that it is when the value of the id element of the current feature is different from the values of the id element of the previous feature elements.

That may not at all be your requirement so you'll have to adapt the condition to your requirement.

Below is an example of how to add a new element based upon that hypothesis. I'm assuming XSLT2 but it could well be XSLT3 or even XSLT1 and would not change that much.

Am example stylesheet that adds an additional sequence element based upon the working hypothesis that I describe previously could be the following:

<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0"
  exclude-result-prefixes="#all">

  <xsl:output indent="yes" />

  <xsl:template match="/" >
    <xsl:apply-templates />
  </xsl:template>
  
  <xsl:template match="attribute()|node()" >
    <xsl:copy>
        <xsl:apply-templates select="@*,node()" />
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="feature" >
    <xsl:copy>
        <xsl:apply-templates select="@*,node()" />
        <xsl:if test="not(. is //feature[1]) 
                      and
                      not(id = ./preceding-sibling::feature/id)" >
          <sequence>second</sequence>
        </xsl:if>
    </xsl:copy>    
  </xsl:template>
  
</xsl:stylesheet>

The input I've used is a version of your input, edited for well-formedness.

<featurecollection>
  <feature> 
    <id>1235</id>    <!-- same id --> 
    <time>100</time> <!-- older --> 
  </feature>
  
  <feature>
    <id>1235</id>    <!-- same id --> 
    <time>101</time> <!-- newer --> 
  </feature>
  
  <feature>
    <id>1236</id>    <!-- unique id --> 
    <time>102</time> <!-- is new --> 
  </feature>
</featurecollection>

with output:

<?xml version="1.0" encoding="UTF-8"?>
<featurecollection>
   <feature>
      <id>1235</id>    <!-- same id --> 
      <time>100</time> <!-- older --> 
   </feature>
   <feature>
      <id>1235</id>    <!-- same id --> 
      <time>101</time> <!-- newer --> 
    </feature>
    <feature>
      <id>1236</id>    <!-- unique id --> 
      <time>102</time> <!-- is new --> 
      <sequence>second</sequence>
   </feature>
</featurecollection>
al.truisme
  • 450
  • 2
  • 11
1

(referring to your "more elaborate version" of the input)

If each feature contains either one or two objects (with identical ids) then you could do simply:

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

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

<xsl:template match="object">
    <xsl:copy>
        <xsl:apply-templates/>
        <sequence>
            <xsl:value-of select="if(position()=last()) then 'second' else 'first'" />
        </sequence>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51