Damo
Damo

Reputation: 1709

XSLT v1.0 - Transformation

I'm a novice using XSLT. My requirement is best expressed through code. I'm looking for advice as to whether its possible to achieve my requirements through XLST. I'm not asking people to write code for me - although code advice would also be appreciated. I'm looking for pointers, e.g. "I need to chain multiple transforms". I'm using XSLT v1.0.

I have the input XML;

<Input>
  <BarId>123</BarId>
  <BarName>myname</BarName>
  <FooName>dummy</FooName>
</Input>

...and need to create the output;

<Out>
  <Foos>
    <Foo>
      <Id>100</Id>
      <Name>dummy</Name>
    </Foo>
  </Foos>
  <Bars>
    <Bar>
      <Id>123</Id>
      <Name>myname</Name>
      <FooId>100</FooId>
    </Bar>
  </Bars>
</Out>

Key Point:

Creating the Foo element is the part I need help with.

The Foo element(s) most precede the Bar element(s).

I'm not overly concerned about generating the Foo Id (i.e. in above example 100) - I have a few options here - but I guess the approach for populating the FooId on the Bar will depend on the overall approach?

(ps. apologies for vague title - Ill update accordingly based on resolution)

Upvotes: 2

Views: 157

Answers (2)

Jukka Matilainen
Jukka Matilainen

Reputation: 10188

I am assuming you want to output a Foo element with a unique ID for each distinct FooName in the input. For this, you could use the Muenchian method:

First you'd build a xsl:key using which you can quickly retrieve FooName elements by their content.

<xsl:key name="FooNames" match="FooName" use="."/>

Using this key, you can then process only the first instance of each FooName, thus getting a distinct list. Here you can make use of the generate-id() function for comparing nodes for identity.

<xsl:apply-templates select="//FooName
                             [generate-id() = 
                              generate-id(key('FooNames', .)[1])]"/>

For generating the identifiers, if you don't care that the generated identifiers may vary from run to run, you can again use the generate-id() function.

Putting it all together:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:key name="FooNames" match="FooName" use="."/>

  <xsl:template match="/">
    <Out>
      <Foos>
        <xsl:apply-templates select="//FooName[generate-id() = 
                                     generate-id(key('FooNames', .)[1])]"/>
      </Foos>
      <Bars>
        <xsl:apply-templates select="//Input"/>
      </Bars>
    </Out>
  </xsl:template>

  <xsl:template match="FooName">
    <Foo>
      <Id><xsl:value-of select="generate-id()"/></Id>
      <Name><xsl:value-of select="."/></Name>
    </Foo>
  </xsl:template>

  <xsl:template match="Input">
    <Bar>
      <Id><xsl:value-of select="BarId"/></Id>
      <Name><xsl:value-of select="BarName"/></Name>
      <FooId>
        <xsl:value-of select="generate-id(key('FooNames', FooName)[1])"/>
      </FooId>
    </Bar>
  </xsl:template>
</xsl:stylesheet>

With the following sample input:

<Inputs>
  <Input>
    <BarId>123</BarId>
    <BarName>myname</BarName>
    <FooName>dummy</FooName>
  </Input>
  <Input>
    <BarId>124</BarId>
    <BarName>anothername</BarName>
    <FooName>dummy</FooName>
  </Input>
  <Input>
    <BarId>125</BarId>
    <BarName>yetanothername</BarName>
    <FooName>dummy-two</FooName>
  </Input>
</Inputs>

It will yield the output:

<Out>
  <Foos>
    <Foo>
      <Id>id213296</Id>
      <Name>dummy</Name>
    </Foo>
    <Foo>
      <Id>id214097</Id>
      <Name>dummy-two</Name>
    </Foo>
  </Foos>
  <Bars>
    <Bar>
      <Id>123</Id>
      <Name>myname</Name>
      <FooId>id213296</FooId>
    </Bar>
    <Bar>
      <Id>124</Id>
      <Name>anothername</Name>
      <FooId>id213296</FooId>
    </Bar>
    <Bar>
      <Id>125</Id>
      <Name>yetanothername</Name>
      <FooId>id214097</FooId>
    </Bar>
  </Bars>
</Out>

Upvotes: 1

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243459

It can be as simple as this, assuming a BarName precedes its corresponding FooName:

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

 <xsl:template match="/*">
     <Out>
      <Foos>
       <xsl:apply-templates select="FooName"/>
      </Foos>
      <Bars>
       <xsl:apply-templates select="BarName"/>
      </Bars>
     </Out>
 </xsl:template>

 <xsl:template match="FooName">
  <Foo>
    <Id><xsl:call-template name="makeFooId"/></Id>
    <Name><xsl:value-of select="."/></Name>
  </Foo>
 </xsl:template>
 <xsl:template match="BarName">
   <Bar>
    <Id><xsl:value-of select="preceding-sibling::BarId[1]"/></Id>
    <Name><xsl:value-of select="."/></Name>
    <FooId>
      <xsl:call-template name="makeFooId">
        <xsl:with-param name="pFoo" select="following-sibling::FooName[1]"/>
      </xsl:call-template>
    </FooId>
  </Bar>
 </xsl:template>

 <xsl:template name="makeFooId">
  <xsl:param name="pFoo" select="."/>

  <!-- Replace the following line with your id - generating code -->
  <xsl:text>100</xsl:text>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied to the provided XML document:

<Input>
    <BarId>123</BarId>
    <BarName>myname</BarName>
    <FooName>dummy</FooName>
</Input>

the wanted, correct result is produced:

<Out>
   <Foos>
      <Foo>
         <Id>100</Id>
         <Name>dummy</Name>
      </Foo>
   </Foos>
   <Bars>
      <Bar>
         <Id>123</Id>
         <Name>myname</Name>
         <FooId>100</FooId>
      </Bar>
   </Bars>
</Out>

Upvotes: 0

Related Questions