serilain
serilain

Reputation: 451

XSL template match on attribute of parent

I have some XML that looks like this:

  <section class="DoCO:Section">
    <h1 class="DoCO:SectionTitle" id="42" page="3" column="2">EXPERIMENT</h1>
    <region class="DoCO:TextChunk" id="43" page="3" column="2">lots of body<xref ref-type="bibr" rid="R7" id="35" class="deo:Reference">7</xref> text</region>
    <region class="DoCO:FigureBox" id="F4">
      <image class="DoCO:Figure" src="2cn.page_003.image_04.png" thmb="2cn.page_003.image_04-thumb.png"/>
      <caption class="deo:Caption" id="44" page="3" column="2">Figure 4: Experimental Setup</caption>
    </region>

I had been using the following XSL to match on the xref elements individually:

                <xsl:for-each select="article/body/section">
                    <sec>
                        <xsl:for-each select="h1">
                            <title>
                                <xsl:value-of select="string(.)"/>
                            </title>
                        </xsl:for-each> 

                        <xsl:for-each select="region">
                            <p>
                                <xsl:apply-templates/>
                            </p>
                        </xsl:for-each>
</xsl:template>
<xsl:template match="xref">
    <xref/>
</xsl:template>

However, I want to be able to group the image and caption elements together within a given region without changing the very open-ended way of handling region elements currently, so I'm trying to do the following:

<xsl:template match="@class[.='DoCO:FigureBox']">
    <fig xmlns:xlink="http://www.w3.org/1999/xlink">
        <graphic>
            <xsl:for-each select="image">
                <xsl:attribute name="xlink:href">
                    <xsl:value-of select="@src"/>
                </xsl:attribute>
            </xsl:for-each>
        </graphic>
        <caption>
            <xsl:for-each select="caption">
                <xsl:value-of select="string(.)"/>
            </xsl:for-each>
        </caption>
    </fig>
</xsl:template>

But the match="@class[.='DoCO:FigureBox']" doesn't appear to be firing. Is it not possible to match on an attribute of the parent of xsl:apply-templates the same way you can match on a child element?

Thanks!

Upvotes: 0

Views: 1964

Answers (1)

Tim C
Tim C

Reputation: 70598

There is nothing wrong with the following syntax:

<xsl:template match="@class[.='DoCO:FigureBox']">

Your (first) problem, lies here

<xsl:apply-templates/>

This is a short-hand for this

<xsl:apply-templates select="node()" />

Which means, you are not selecting any attributes, and so your template match for the attribute @class will not be called.

Now, you could change it to this

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

But this leads on to a second problem. Within the template matching @class, you have a couple of xsl:for-each statements, for image and caption. But at this point, you are positioned on the @class attribute, not a region elements, and so these xsl:for-each statements will find nothing.

What you probably should be doing, is that instead of doing <xsl:for-each select="region"> in your main code, do <xsl:apply-templates select="region" />. Then, you can have two templates, like so

<xsl:template match="region">
   <p>
        <xsl:apply-templates />
   </p>
</xsl:template>

<xsl:template match="region[@class='DoCO:FigureBox']">
    <fig xmlns:xlink="http://www.w3.org/1999/xlink">
        <graphic>
            <xsl:for-each select="image">
                <xsl:attribute name="xlink:href">
                    <xsl:value-of select="@src"/>
                </xsl:attribute>
            </xsl:for-each>
        </graphic>
        <caption>
            <xsl:for-each select="caption">
                <xsl:value-of select="string(.)"/>
            </xsl:for-each>
        </caption>
    </fig>
</xsl:template>

The XSLT processor should give priority to the more specific template in this case, so it will override your default handling of the region element.

In fact, if you are always going to have one caption and image per region, you could simplify the template to this:

<xsl:template match="region[@class='DoCO:FigureBox']">
    <fig xmlns:xlink="http://www.w3.org/1999/xlink">
        <graphic xlink:href="{@src}" />
        <caption>
            <xsl:value-of select="caption" />
        </caption>
    </fig>
</xsl:template>

Notice the use of Attribute Value Templates in creating the attribute. The curly braces indicate and expression to be evaluated, not output literally.

Upvotes: 2

Related Questions