我目前有一个XML文件,如下所示:
<Plan>
<People>
<Person>
<name>Fred Bloggs</name>
<position>CEO</position>
<responsibility>Everything</responsibility>
</Person>
<Person>
<name>Joe Bloggs</name>
<position>Cleaner</position>
<responsibility>Cleaning</responsibility>
</Person>
<Person>
<name>Wilma Bloggs</name>
<position>CTO</position>
<responsibility>Tech stuff</responsibility>
</Person>
<Person>
<name>Betty Bloggs</name>
<position>MD</position>
<responsibility>Management</responsibility>
</Person>
</People>
</Plan>
实际上它是一个人员列表,每个人都有一个字段列表。字段列表将来会扩展,但每个Person都将具有相同的字段。
我想使用XSLT和XSL-FO对其进行转换和格式化,以生成包含四列的PDF,每行列出字段名称,然后列出三个Person对象的字段值。例如,由于上面列出了四个人,我希望表格如下所示:
Name Fred Bloggs Joe Bloggs Wilma Bloggs
Position CEO Cleaner CTO
Responsibility Everything Cleaning Tech stuff
Name Betty Bloggs
Position MD
Responsibility Management
实际上,在XML文件中我按人分组字段(因此每个人都有三个字段),而在最终输出中我想按字段对人进行分组(因此每行对三个不同的人具有相同的字段)
如果我手动编写XSL-FO代码,我可以通过为每行生成这些行的标记来实现这一点:
<fo:table-row>
<fo:table-cell>
<fo:block>Name</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>Fred Bloggs</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>Joe Bloggs</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>Wilma Bloggs</fo:block>
</fo:table-cell>
</fo:table-row>
这给了我想要的结果,所以我知道XSL-FO到PDF部分工作正常,但我需要自动化这个过程。我可以通过XSLT直接实现吗?
我能想到的另一个选择是生成一个单独的XML文件,该文件显式匹配布局结构然后对其进行转换,例如:
<PersonTable>
<PersonRow>
<name1>Fred Bloggs</name1>
<name2>Joe Bloggs</name2>
<name3>Wilma Bloggs</name3>
</PersonRow>
</PersonTable>
最后一个最不受欢迎的选择是自动生成完整的XSL-FO数据,完全侧面转换步骤(即我有效地从XSL-FO转到PDF)。我不想这样做,因为从长远来看,我希望能够在不修改软件的情况下发送不同的XSL模板文件,但如果以前的那些不可能,我可以回到这个选项
答案 0 :(得分:1)
为了简化问题(对我而言),以下样式表以请求的格式创建一个简单的HTML表:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="cols" select="3" />
<xsl:template match="People">
<table border="1">
<xsl:apply-templates select="Person[position() mod $cols = 1]"/>
</table>
</xsl:template>
<xsl:template match="Person">
<xsl:variable name="group" select=". | following-sibling::Person[position() < $cols]" />
<xsl:for-each select="*">
<xsl:variable name="i" select="position()" />
<tr>
<td>
<xsl:value-of select="name()"/>
</td>
<xsl:for-each select="$group">
<td>
<xsl:value-of select="*[$i]"/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
应用于您的示例,结果为:
<?xml version="1.0" encoding="UTF-8"?>
<table border="1">
<tr>
<td>name</td>
<td>Fred Bloggs</td>
<td>Joe Bloggs</td>
<td>Wilma Bloggs</td>
</tr>
<tr>
<td>position</td>
<td>CEO</td>
<td>Cleaner</td>
<td>CTO</td>
</tr>
<tr>
<td>responsibility</td>
<td>Everything</td>
<td>Cleaning</td>
<td>Tech stuff</td>
</tr>
<tr>
<td>name</td>
<td>Betty Bloggs</td>
</tr>
<tr>
<td>position</td>
<td>MD</td>
</tr>
<tr>
<td>responsibility</td>
<td>Management</td>
</tr>
</table>
呈现为:
答案 1 :(得分:1)
从two adjacent tables in body-region each with two columns(xsl-fo)调整我的解决方案并仍在使用XSLT 1.0:
<xsl:param name="cols" select="3" />
<xsl:template match="People">
<fo:table>
<fo:table-body>
<xsl:call-template name="rows" />
</fo:table-body>
</fo:table>
</xsl:template>
<xsl:template name="rows">
<xsl:param name="persons" select="*" />
<xsl:call-template name="row-group">
<xsl:with-param name="persons"
select="$persons[position() <= $cols]" />
</xsl:call-template>
<xsl:if test="count($persons) > $cols">
<xsl:call-template name="rows">
<xsl:with-param name="persons" select="$persons[position() > $cols]" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="row-group">
<xsl:param name="persons" />
<xsl:for-each select="$persons[1]/*">
<xsl:variable name="position" select="position()" />
<fo:table-row>
<fo:table-cell>
<fo:block><xsl:value-of select="local-name()" /></fo:block>
</fo:table-cell>
<xsl:for-each select="$persons">
<fo:table-cell>
<fo:block><xsl:apply-templates select="./*[position()= $position]" /></fo:block>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
</xsl:for-each>
</xsl:template>