I'd like to use an xml file
<pics>
<pic no="1">c:\pic1.jpg</pic>
<pic no="2">c:\pic2.jpg</pic>
<pic no="3">c:\pic3.jpg</pic>
<pic no="4">c:\pic4.jpg</pic>
<pic no="5">c:\pic5.jpg</pic>
....
</pics>
in an html table:
<table cellspacing="2" cellpadding="2" border="0">
<tr>
<td><img src="" width="150" height="120" /></td>
<td><img src="" width="150" height="120" /></td>
<td><img src="" width="150" height="120" /></td>
</tr>
<tr>
<td><img src="from xml" width="150" height="120" /></td>
<td><img src="from xml" width="150" height="120" /></td>
<td><img src="from xml" width="150" height="120" /></td>
</tr>
<tr>
<td><img src="from xml" width="150" height="120" /></td>
<td><img src="from xml" width="150" height="120" /></td>
<td><img src="from xml" width="150" height="120" /></td>
</tr>
</table>
Whats the best way to do this?
XML:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="web_page.xsl"?>
<pics>
<pic>
<td>
<no src="http://farm1.static.flickr.com/160/387667598_ea86c93d81.jpg" width="150" height="120">1</no>
</td>
<td>
<no src="http://farm1.static.flickr.com/160/387667598_ea86c93d81.jpg" width="150" height="120">2</no>
</td>
<td>
<no src="http://farm1.static.flickr.com/160/387667598_ea86c93d81.jpg" width="150" height="120">3</no>
</td>
</pic>
<pic>
<td>
<no src="http://motherjones.com/files/legacy/mojoblog/funny-cats-a10.jpg" width="150" height="120">4</no>
</td>
<td>
<no src="http://motherjones.com/files/legacy/mojoblog/funny-cats-a10.jpg" width="150" height="120">5</no>
</td>
<td>
<no src="http://motherjones.com/files/legacy/mojoblog/funny-cats-a10.jpg" width="150" height="120">6</no>
</td>
</pic>
</pics>
XSLT:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<table>
<xsl:for-each select="pics/pic">
<tr>
<xsl:for-each select="td">
<td><img>
<xsl:attribute name="src">
<xsl:value-of select="no//@src"/>
</xsl:attribute>
<xsl:attribute name="width">
<xsl:value-of select="no//@width"/>
</xsl:attribute>
<xsl:attribute name="height">
<xsl:value-of select="no//@height"/>
</xsl:attribute>
</img></td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Try it yourself here (copy and paste my code into the appropriate boxes):
There's a bug in the suggested output, as <img/>
elements must have alt
attributes in every version of HTML in which they are present.
Anyway the following does this but without those attributes that can be done from CSS instead (just to keep size down). Adding them back in if desired is trivial:
<xsl:template match="pics">
<table>
<xsl:apply-templates select="pic[position() mod 3 = 1]"/>
</table>
</xsl:template>
<xsl:template match="pic[position() mod 3 = 1]">
<tr>
<td>
<xsl:if test="2 > count(following-sibling::pic)">
<xsl:attribute name="colspan">
<xsl:value-of select="3 - count(following-sibling::pic)"/>
</xsl:attribute>
</xsl:if>
<img src="{.}" alt="" />
</td>
<xsl:apply-templates select="following-sibling::pic[3 > position()]" />
</tr>
</xsl:template>
<xsl:template match="pic">
<td><img src="{.}" alt=""/></td>
</xsl:template>
The above assumes you want the path from the file used directly, adding code to transform it in some way (say taking just the last part of the path using substring-after()
) isn't a difficult extension, assuming said transform isn't complicated itself.
Edit:
Myself and JohnB are going into further territory here, the above suffices to answer the original question.
Added to give a fuller answer to JohnB's question. The following is the equivalent code using for-each instead of apply-templates. In theory both a sequential and a state-machine base implementation of an XSLT processor should deal with this identically, though you may find differences in practice (if you told me they were different with a given processor I'd bet a small amount on it being slightly faster with sequential processing and slightly slower with state-machine processing, but I'd only bet a very small amount).
Note that we can't reuse the default template for pic. On the plus-side, if we have a different default template for pic elsewhere (if this were part of a much more complicated stylesheet), we don't need to be clever to differentiate between them, which is the main time that I personally would lean toward for-each.
<xsl:template match="pics">
<table>
<xsl:for-each select="pic[position() mod 3 = 1]">
<tr>
<td>
<xsl:if test="2 > count(following-sibling::pic)">
<xsl:attribute name="colspan">
<xsl:value-of select="3 - count(following-sibling::pic)"/>
</xsl:attribute>
</xsl:if>
<img src="{.}" alt="" />
</td>
<xsl:for-each select="following-sibling::pic[3 > position()]">
<td><img src="{.}" alt=""/></td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
Use XSL. Example here. Btw why do you have the no
attribute there ?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With