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><开发者_JS百科;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?
- Very similar example (and shameless plug)
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):
- w3schools.com
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 ?
Here is a typical solution that adheres to the spirit of XSLT (no <xsl:for-each>
), as short as possible, and parameterized on the desired number of columns in the table.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pNumCols" select="3"/>
<xsl:template match="pics">
<table cellspacing="2" cellpadding="2" border="0">
<xsl:apply-templates select="pic[position() mod $pNumCols = 1]"/>
</table>
</xsl:template>
<xsl:template match="pic">
<tr>
<xsl:apply-templates mode="process" select=
"(. | following-sibling::pic)[not(position() > $pNumCols)]"/>
</tr>
</xsl:template>
<xsl:template match="pic" mode="process">
<td><img src="{.}" width="150" height="120" /></td>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document (based on the provided XML document, but with more pictures that are really colorful and interesting):
<pics>
<pic no="1">http://col.stb.s-msn.com/i/D7/6A19748C9AA58B938F42099543D2E.jpg</pic>
<pic no="2">http://col.stb.s-msn.com/i/1F/35A8478AC24EEF95933B5F0E4E394.jpg</pic>
<pic no="3">http://col.stb.s-msn.com/i/76/3ADA01320CEC8B31D53FACC0C11E.jpg</pic>
<pic no="4">http://col.stb.s-msn.com/i/92/51BF117987A3279571F06BEB4AE39D.jpg</pic>
<pic no="5">http://col.stb.s-msn.com/i/9B/9A6E876BA2F7EAE82392C7E7F6C1C.jpg</pic>
<pic no="6">http://col.stb.s-msn.com/i/50/8CC964E5503A7F61F8AD22A12024.jpg</pic>
<pic no="7">http://col.stb.s-msn.com/i/C4/F7EF634B7084DA69AAB5AAD05C8922.jpg</pic>
<pic no="8">http://col.stb.s-msn.com/i/FB/C8367425D67FA391A5E0F8A3E0276B.jpg</pic>
</pics>
the wanted result is produced (see it also in a browser :) ) :
<table cellspacing="2" cellpadding="2" border="0">
<tr>
<td>
<img src="http://col.stb.s-msn.com/i/D7/6A19748C9AA58B938F42099543D2E.jpg" width="150" height="120"/>
</td>
<td>
<img src="http://col.stb.s-msn.com/i/1F/35A8478AC24EEF95933B5F0E4E394.jpg" width="150" height="120"/>
</td>
<td>
<img src="http://col.stb.s-msn.com/i/76/3ADA01320CEC8B31D53FACC0C11E.jpg" width="150" height="120"/>
</td>
</tr>
<tr>
<td>
<img src="http://col.stb.s-msn.com/i/92/51BF117987A3279571F06BEB4AE39D.jpg" width="150" height="120"/>
</td>
<td>
<img src="http://col.stb.s-msn.com/i/9B/9A6E876BA2F7EAE82392C7E7F6C1C.jpg" width="150" height="120"/>
</td>
<td>
<img src="http://col.stb.s-msn.com/i/50/8CC964E5503A7F61F8AD22A12024.jpg" width="150" height="120"/>
</td>
</tr>
<tr>
<td>
<img src="http://col.stb.s-msn.com/i/C4/F7EF634B7084DA69AAB5AAD05C8922.jpg" width="150" height="120"/>
</td>
<td>
<img src="http://col.stb.s-msn.com/i/FB/C8367425D67FA391A5E0F8A3E0276B.jpg" width="150" height="120"/>
</td>
</tr>
</table>
Do note:
The use of the XPath
mod
operator to determine the items of each row.The use of modes to process the same type of element (
<pic>
) in two different ways.The use of AVT (attribute-value-templates) to make the code shorter and more understandable.
精彩评论