views:

55

answers:

2

I'm trying to add different tags to a node depending on an attribute value of its grandchild node.

Sample Input (a 1x3 table):

<table>
    <row>
        <cell row="1" column="1" >heading text one</cell>
    </row>

    <row>
        <cell row="2" column="1" >body text one</cell>
    </row>
    <row>
        <cell row="3" column="1" >body text two</cell>
    </row>
</table>

Need output like this:

<TableElmt>
    <HeadingElmt>
        <RowElmt>
            <CellElmt>heading text one</CellElmt>
        </RowElmt>
    </HeadingElmt>

    <BodyElmt>
        <RowElmt>
            <CellElmt>body text one</CellElmt>
        </RowElmt>
        <RowElmt>
            <CellElmt>body text two</CellElmt>
        </RowElmt>
    </BodyElmt>
</TableElmt>

Basically I can only decide if the row is a heading row based on the @row element of the cell.

Here's what I've tried:

<xsl:template name="matcheverything" match="table">
    <xsl:apply-templates select="row" />
</xsl:template>

<xsl:template name="matchheadings" match="table[*/*/@row=1]">
    <BodyElmt>
        <xsl:apply-templates select="row" />
    </BodyElmt>
</xsl:template>

<xsl:template match="row">
    <xsl:choose>
        <xsl:when test="*/@row=1">
            <HeadingElmt><RowElmt>
                <xsl:apply-templates select="cell"/>
            </RowElmt></HeadingElmt>
        </xsl:when>
        <xsl:otherwise>
            <RowElmt>
                <xsl:apply-templates select="cell"/>
            </RowElmt>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="cell">
    <CellElmt><xsl:apply-templates select="*"/></CellElmt>
</xsl:template>

I was thinking the "matchheadings" template, having a more specific match requirement, should recognize the heading row, however it's actually matching every single row in the table.

So my actual out put from this stylesheet is every row treated as a heading row - very bad :(

<TableElmt>
    <HeadingElmt>
        <RowElmt>
            <CellElmt>heading text one</CellElmt>
        </RowElmt>

        <RowElmt>
            <CellElmt>body text one</CellElmt>
        </RowElmt>
        <RowElmt>
            <CellElmt>body text two</CellElmt>
        </RowElmt>
    </HeadingElmt>
</TableElmt>
+1  A: 

Edit: It looks like I missed this

Basically I can only decide if the row is a heading row based on the @row element of the cell.

This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:template match="table">
        <TableElmt>
            <HeadingElmt>
                <xsl:apply-templates select="row[cell/@row=1]"/>
            </HeadingElmt>
            <BodyElmt>
                <xsl:apply-templates select="row[cell/@row!=1]"/>
            </BodyElmt>
        </TableElmt>
    </xsl:template>
    <xsl:template match="row">
        <RowElmt>
            <xsl:apply-templates/>
        </RowElmt>
    </xsl:template>
    <xsl:template match="cell">
        <CellElmt>
            <xsl:apply-templates/>
        </CellElmt>
    </xsl:template>
</xsl:stylesheet>

Output:

<TableElmt>
    <HeadingElmt>
        <RowElmt>
            <CellElmt>heading text one</CellElmt>
        </RowElmt>
    </HeadingElmt>
    <BodyElmt>
        <RowElmt>
            <CellElmt>body text one</CellElmt>
        </RowElmt>
        <RowElmt>
            <CellElmt>body text two</CellElmt>
        </RowElmt>
    </BodyElmt>
</TableElmt>
Alejandro
Works very well, thanks! I haven't thought about testing my row indexes.. but it definitely works in this case.
highlightall
@highlightall: You are wellcome!
Alejandro
+1  A: 

This transformation:

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

 <xsl:template match="table">
  <TableElmt>
   <xsl:apply-templates/>
  </TableElmt>
 </xsl:template>

 <xsl:template match="row[cell/@row='1']">
  <HeadingElmt>
   <xsl:apply-templates select="." mode="copy"/>
  </HeadingElmt>
 </xsl:template>

 <xsl:template match="row[cell[not(@row='1')]][1]">
  <BodyElmt>
   <xsl:apply-templates select=".|following-sibling::row" mode="copy"/>
  </BodyElmt>
 </xsl:template>

 <xsl:template match="row" mode="copy">
   <RowElmt>
     <xsl:apply-templates/>
   </RowElmt>
 </xsl:template>

 <xsl:template match="cell">
   <CellElmt>
    <xsl:value-of select="."/>
   </CellElmt>
 </xsl:template>

 <xsl:template match="row"/>
</xsl:stylesheet>

when applied on the provided XML document:

<table>
    <row>
        <cell row="1" column="1" >heading text one</cell>
    </row>
    <row>
        <cell row="2" column="1" >body text one</cell>
    </row>
    <row>
        <cell row="3" column="1" >body text two</cell>
    </row>
</table>

produces the wanted, correct result:

<TableElmt>
    <HeadingElmt>
        <RowElmt>
            <CellElmt>heading text one</CellElmt>
        </RowElmt>
    </HeadingElmt>
    <BodyElmt>
        <RowElmt>
            <CellElmt>body text one</CellElmt>
        </RowElmt>
        <RowElmt>
            <CellElmt>body text two</CellElmt>
        </RowElmt>
    </BodyElmt>
</TableElmt>
Dimitre Novatchev
@Dimitre: This is exactly what I'm looking for. I guess I was missing mode="copy" and testing for both "row[cell/@row='1']" and "row[cell[not(@row='1')]]". Thanks a lot!
highlightall