Search code examples
xsltxslt-1.0xslt-2.0xsl-fo

Move a block from another add a node as attribute and remove the block


I need to transform this XMl into another by deleting or coping a block while formating this block like the output desired, I'm stuck on how to make this formating

<?xml version="1.0" encoding="ISO-8859-1" ?> 
<output>
    <cars>
        <car>
          <id>1</id>
          <brand>Audi</brand>
          <type>A3</type>
          <license>B-01-TST</license>
        </car>
        <car>
          <id>2</id>
          <brand>Volkwagen</brand>
          <type>Golf</type>
          <license>IF-02-TST</license>
        </car>
    </cars>
    <distances>
        <distance>
          <id_car>1</id_car>
          <date>20110901</date>
          <distance>111</distance>
        </distance>
        <distance>
          <id_car>1</id_car>
          <date>20110902</date>
          <distance>23</distance>
        </distance>
        <id_car>2</id_car>
          <date>20110901</date>
          <distance>92</distance>
        </distance>
        <distance>
          <id_car>2</id_car>
          <date>20110902</date>
          <distance>87</distance>
        </distance>
    </distances>
</output>

Into this output:

<?xml version="1.0" encoding="ISO-8859-1" ?> 
<output>
<cars>
<car>
  <id>1</id>
  <brand>Audi</brand>
  <type>A3</type>
  <license>B-01-TST</license>
  <distances>
    <distance day="20110901">111</distance>
    <distance day="20110902">23</distance>
  </distances>
</car>
<car>
  <id>2</id>
  <brand>Volkwagen</brand>
  <type>Golf</type>
  <license>IF-02-TST</license>
  <distances>
    <distance day="20110901">92</distance>
    <distance day="20110902">87</distance>
  </distances>
</car>
</cars>
</output>

By now I was only able to remove the distances block and add it in the correct place, but how can I get the distances organized by car/id and add it with the date tag as attribute day into the distances ? Here is what I've done so far:

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

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

<xsl:template match="distances" />

<xsl:template match="license">
    <xsl:copy-of select="."/>
    <distances></distances> 
</xsl:template>

</xsl:stylesheet>

Solution

  • Use a key to follow the cross-reference and then write a template to transform those referenced elements:

    <?xml version="1.0" encoding="UTF-8" ?>
    <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    
        <xsl:strip-space elements="*"/>
        <xsl:output indent="yes"/>
    
        <xsl:key name="dist" match="distances/distance" use="id_car"/>
    
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="distances" />
    
        <xsl:template match="license">
            <xsl:next-match/>
            <distances>
                <xsl:apply-templates select="key('dist', ../id)"/>
            </distances> 
        </xsl:template>
    
        <xsl:template match="distance">
            <distance day="{date}">
                <xsl:value-of select="distance"/>
            </distance>
        </xsl:template>
    
    </xsl:transform>
    

    http://xsltransform.net/a9GixV