Home > Software engineering >  Is there a way to make 'xsl:for-each' create one output file for every found instance?
Is there a way to make 'xsl:for-each' create one output file for every found instance?

Time:07-13

#Hi everyone!

Would be grateful for some help in what feels like a slightly too steep learning curve ...

I have three files: '1b.xml', '2b.xml' and 'try.xcl'.

What I want to create is a number of xml and/or html files, each consisting of one of the instances found by the 'xsl:for-each', named after the @date and @id. I also need to use the 'class' value in 'bb' to select the matching 'class' values if both 'gg' and 'vv'.

I want the output to look like this:

<meny>
<mains>
    <meat>chicken</meat>
    <fish>trout</fish>
    <vegetarian>moussaka</vegetarian>
</mains>
<extra>
    <cake>Napoleon cake</cake>
    <drinks>white</drinks>
</extra>

'try.xsl'

<xsl:template match="aa">
  <xsl:variable name="doc2" select="document('2b.xml')"/>
  <body>
    <table>
      <xsl:for-each select="bb">
        <xsl:variable name="bb-num" select="@num"/>
        <xsl:variable name="dd" select="$doc2/cc/dd[@id=$bb-num]"/> 
        <xsl:variable name="bb-class" select="@class"/>
        <xsl:variable name="gg" select="$dd/ff/gg[@class=$bb-class]"/> 
        <xsl:variable name="vv" select="$dd/ff/vv[@class=$bb-class]"/>
         <tr>
           <td><xsl:value-of select="@date"/></td>
           <td><xsl:value-of select="$dd/mm/ee"/></td>
           <td><xsl:value-of select="$gg"/></td>
           <td><xsl:value-of select="$vv"/></td>
         </tr>
       </xsl:for-each>
     </table>        
   </body>
</xsl:template>

'1b.xml':

    <aa>
    <bb date="31.12.2022" num="prs1" ></bb>
    <bb date="07.01.2023" num="prs2" ></bb>
</aa>

'2b.xml':

<cc>
    <dd id="prs1">
        <mm>
            <ee>steak</ee>
            <nn>salmon</nn>
            <oo>falaffel</oo>
        </mm>
        <ff>
            <gg >carrot cake</gg>
            <gg >chocolate cake</gg>
            <gg >cream cake</gg>
            <vv >red</vv>
            <vv >white</vv>
            <vv >sparkle</vv>  
        </ff>
    </dd>
    <dd id="prs2">
        <mm>
            <ee>chicken</ee>
            <nn>trout</nn>
            <oo>moussaka</oo>
         </mm>        
         <ff>
            <gg >Swiss roll</gg>
            <gg >Napoleon cake</gg>
            <gg >marzipan cake</gg>
            <vv >red</vv>
            <vv >white</vv>
            <vv >sparkle</vv>            
          </ff>
      </dd>    
  </cc>

CodePudding user response:

--- edited in response to clariifcation ---

In XSLT 2.0 or higher, you can do:

<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:key name="dd" match="dd" use="@id" />
<xsl:key name="gg" match="gg" use="@class" />
<xsl:key name="vv" match="vv" use="@class" />
        
<xsl:template match="/aa">
    <xsl:for-each select="bb">
        <xsl:result-document href="{@date}-{@num}.xml">
            <xsl:variable name="dd" select="key('dd', @num, document('2b.xml'))" />
            <menu>
                <mains>
                    <meat>
                        <xsl:value-of select="$dd/mm/ee" />
                    </meat>
                    <fish>
                        <xsl:value-of select="$dd/mm/nn" />
                    </fish>
                    <vegetarian>
                        <xsl:value-of select="$dd/mm/oo" />
                    </vegetarian>
                </mains>
                <extra>
                    <cake>
                        <xsl:value-of select="key('gg', @class, $dd)" />
                    </cake>
                    <drinks>
                        <xsl:value-of select="key('vv', @class, $dd)" />
                    </drinks>
                </extra>
            </menu>
        </xsl:result-document>
    </xsl:for-each>
</xsl:template>
  
</xsl:stylesheet>

Using your example inputs, this will create 2 result documents:

31.12.2022-prs1.xml

 <menu>
  <mains>
     <meat>steak</meat>
     <fish>salmon</fish>
     <vegetarian>falaffel</vegetarian>
  </mains>
  <extra>
     <cake>chocolate cake</cake>
     <drinks>white</drinks>
  </extra>
</menu>

07.01.2023-prs2.xml

<menu>
  <mains>
     <meat>chicken</meat>
     <fish>trout</fish>
     <vegetarian>moussaka</vegetarian>
  </mains>
  <extra>
     <cake>Swiss roll</cake>
     <drinks>red</drinks>
  </extra>
</menu>

These results are different from the one you posted, nevertheless I believe they are correct.


P.S. The answer above is practically the same as the answer I gave to your previous question here. The adjustments are rather trivial and I wonder why you couldn't do them yourself.

  • Related