In XSL: How to avoid choose-blocks for wrapping el

2019-02-23 01:39发布

问题:

there is a case, that appears often times. I am parsing an XML and generate my XHTML document via XSLT 1.0.

Case:

/* XML */
<Image src="path-to-img.jpg" href="link-to-page.html" />

/* XSL */
<xsl:choose>
    <xsl:when test="@href">
    <a href="{@href}">
       <img src="{@src}"/>
    </a>
</xsl:when>
<xsl:otherwise>
    <img src="{@src}"/>
</xsl:otherwise>
</xsl:choose>

You see the problem: I am just fetching the case if there is a href set. I'm not satisfied with this approach, but I don't see another option for implementing this.

Any ideas?

回答1:

The way to eliminate explicit conditional instructions inside a template is to use pattern-matching within the match pattern of a template:

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

 <xsl:template match="Image[@href]">
    <a href="{@href}">
     <xsl:call-template name="basicImage" />
    </a>
 </xsl:template>

 <xsl:template match="Image" name="basicImage">
   <img src="{@src}"/>
 </xsl:template>
</xsl:stylesheet>

XSLT 2.0: There is an especially ellegant solution using <xsl:next-match>:

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

 <xsl:template match="Image[@href]">
    <a href="{@href}">
    <xsl:next-match/>
    </a>
 </xsl:template>

 <xsl:template match="Image" name="basicImage">
   <img src="{@src}"/>
 </xsl:template>
</xsl:stylesheet>

Both transformations, when applied on the provided XML document:

<Image src="path-to-img.jpg" href="link-to-page.html" />

produce the wanted, correct result:

<a href="link-to-page.html">
   <img src="path-to-img.jpg"/>
</a>


回答2:

To avoid duplication of code, you could make use of named templates, to contain the code for rendering the img tag

<xsl:template name="img">
   <img src="{@src}" />
</xsl:template>

You can then call it like so

<xsl:call-template name="img" />

So, the finished XSLT would be

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <xsl:output method="xml" indent="yes"/>

   <xsl:template match="/Image ">
      <xsl:choose>
         <xsl:when test="@href">
            <a href="{@href}">
               <xsl:call-template name="img" />
            </a>
         </xsl:when>
         <xsl:otherwise>
            <xsl:call-template name="img" />
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>

   <xsl:template name="img">
      <img src="{@src}" />
   </xsl:template>
</xsl:stylesheet>

Although you still have two xsl:call-template commands, any changes to the rendering of the img tag now only have to be done in one place. You could also call this template from anywhere within your XSLT, assuming the current node is an image node.



回答3:

Option #1 you can create non-conditional a and image bellow and using xsl:if append attribute (note in this case there is some lack - always exists tag a but without href - for end user it is not a problem):

<a>
   <xsl:if test="@href">
      <xsl:attribute name="href" value="{@href}"/>
   </xsl:if>
   <img src="{@src}"/>

</a>

Option #2 If javascript is applicable - just place to img handling of onclick



标签: xml xslt