Re: [xsl] repeat and replace loop ( at the same time )

Subject: Re: [xsl] repeat and replace loop ( at the same time )
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Tue, 2 Apr 2002 16:41:45 +0100
Hi PG,

> I need to repeat an XML record and replace it at the same time with
> different data.

In your example, the driver for the number of times that you want to
"repeat" the record is 'replace_data.xml'. Since you're working with
two documents, I recommend that you create global variables to hold
the relevant nodes of each -- the record elements:

<xsl:variable name="template" select="/root/record" />
<xsl:variable name="data"
              select="document('replace_data.xml')/root/record" />

You should then apply templates to the record elements from
'replace_data.xml', as you are doing:

<xsl:template match="/">
  <root>
    <xsl:apply-templates select="$data" />
  </root>
</xsl:template>

The template for the record needs to basically copy the template
record, but using the information from the current record. The
template needs to match record elements and create a record element:

<xsl:template match="record">
  <record>
    ...
  </record>
</xsl:template>

For the content of the record, you need to process the content of the
template record element, but in doing so, the processor needs to be
aware of the data from the current record. You could store it in a
variable, and then iterate over the content of the template, with
something like:

<xsl:template match="record">
  <record>
    <xsl:variable name="record" select="." />
    <xsl:for-each select="$template/namedcell">
      ...
    </xsl:for-each>
  </record>
</xsl:template>

Or you could apply templates to the content of the template, passing
the record as a parameter, with something like:

<xsl:template match="record">
  <record>
    <xsl:apply-templates select="$template/*">
      <xsl:with-param name="record" />
    </xsl:apply-templates>
  </record>
</xsl:template>

<xsl:template match="data" />

<xsl:template match="namedcell">
  <xsl:param name="record" />
  ...
</xsl:template>

Either way, you then need to copy the current namedcell element:

  <xsl:copy-of select="." />

And then you need to identify the relevant child element of the record
element, named by current namedcell element. You can find this by
getting all the child elements of the record and then filtering that
node set to retrieve only those whose name is the same as the name
attribute of the current node, with:

  $record/*[name() = current()/@name]

Assuming that you want to copy the existing data element if there is
no such element, you could use something like:

  <xsl:variable name="value"
                select="$record/*[name() = current()/@name]" />
  <xsl:choose>
    <xsl:when test="$value">
      <data><xsl:value-of select="$value" /></data>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy-of select="following-sibling::data[1]" />
    </xsl:otherwise>
  </xsl:choose>

How you actually handle the content of the template is up to you, of
course. The principal of working with separate template and data is:

  - process the data to provide the repetition
  - process the template to provide the structure
  - process the data to provide the values

Getting from the data to the template to the data again means keeping
track of where you are, which you can do with variables or parameters.

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread