XML转XML-FO生成PDF:xsl:choose仅匹配首个元素的问题排查
解决XSLT处理多语言
<name>元素的问题 我正在用XSLT把XML转换成XML-FO来生成PDF,表格是3列布局,前4行的第2、3列处于合并状态。但现在遇到个问题:处理<names>节点下的多个<name>元素时,只能匹配到第一个,第二个<name>完全无法被正确处理。
我的XML结构
<WS-Standards> <tags> <tag category = "parameters" > <!-- WS_Beer_Type --> <tag_name>WS_Beer_TypeX</tag_name> <!-- browsename --> <tag_number>30004</tag_number> <datatype>Unsigned32</datatype> <accessrights>RW</accessrights> <names> <name language="DE">Biersorte</name> <name language="EN">Beer Type</name> </names> </tag> </tags> </WS-Standards>
当前使用的XSLT(部分截断)
<?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version="1.0" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" version="1.0" indent="yes"/> <xsl:template match="WS-Standards"> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master page-height="297mm" page-width="210mm" margin="5mm 25mm 5mm 25mm" master-name="PageMaster"> <fo:region-body margin="20mm 0mm 20mm 0mm"/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="PageMaster"> <fo:flow flow-name="xsl-region-body"> <fo:block> <xsl:apply-templates select="tags"/> </fo:block> </fo:flow> </fo:page-sequence> </fo:root> </xsl:template> <xsl:template match="tags"> <fo:block> <xsl:apply-templates select="tag"/> </fo:block> </xsl:template> <xsl:template match="tag"> <fo:block space-before="6pt" border-top="3pt solid green"> <fo:table> <fo:table-column column-number="1" column-width="20%" border-style="solid" border-width="1pt"/> <fo:table-column column-number="2" column-width="10%" border-style="solid" border-width="1pt"/> <fo:table-column column-number="3" column-width="70%" border-style="solid" border-width="1pt"/> <fo:table-body> <xsl:apply-templates/> </fo:table-body> </fo:table> </fo:block> </xsl:template> <xsl:template match="tag_name"> <fo:table-row> <fo:table-cell column-number="1" border="1pt solid black"> <fo:block> Tag Name </fo:block> </fo:table-cell> <fo:table-cell column-number="2" number-columns-spanned="2" border="1pt solid black"> <fo:block> <xsl:value-of select="."/> </fo:block> </fo:table-cell> </fo:table-row> </xsl:template> <xsl:template match="tag_number"> <fo:table-row> <fo:table-cell column-number="1" border="1pt solid black"> <fo:block> Tag-Nummer </fo:block> </fo:table-cell> <fo:table-cell column-number="2" number-columns-spanned="2" border="1pt solid black"> <fo:block> <xsl:value-of select="."/> </fo:block> </fo:table-cell> </fo:table-row> </xsl:template> <xsl:template match="datatype"> <fo:table-row> <fo:table-cell column-number="1" border="1pt solid black"> <fo:block> Datentyp </fo:block> </fo:table-cell> <fo:table-cell column-number="2" number-columns-spanned="2" border="1pt solid black"> <fo:block> <xsl:value-of select="."/> </fo:block> </fo:table-cell> </fo:table-row> </xsl:template> <xsl:template match="accessrights"> <fo:table-row> <fo:table-cell column-number="1" border="1pt solid black"> <fo:block> Zugriffsrechte </fo:block> </fo:table-cell> <fo:table-cell column-number="2" number-columns-spanned="2" border="1pt solid black"> <fo:block> <xsl:value-of select="."/> </fo:block> </fo:table-cell> </fo:table-row> </xsl:template> <xsl:template match="names"> <xsl:choose> <xsl:when test="number(./name/position()) = 1"> <fo:table-row> <fo:table-cell column-number="1" border="1pt solid black"> <fo:block> Name </fo:block> </fo:table-cell> <fo:table-cell column-number="2" border="1pt solid black"> <fo:block> <xsl:value-of select="./name/@language"/> </fo:block> </fo:table-cell> <fo:table-cell column-number="3" border="1pt solid black"> <fo:block> <xsl:value-of select="./name"/> </fo:block> </fo:table-cell> </fo:table-row> </xsl:when> <xsl:otherwise> <fo:table-row> <fo:table-cell column-number="1" border="1pt solid black"> <fo:block> NameXXX </fo:block> </fo:table-cell> <fo:table-cell column-number="2" border="1pt
问题分析
你之前的写法核心问题在于:直接匹配<names>节点后,用<xsl:choose>判断第一个<name>的位置,但<names>节点本身只会被处理一次,没法遍历内部的多个<name>元素。而且./name/position()这种写法返回的是整个<name>集合的位置(永远为1),所以只会触发<xsl:when>的逻辑,完全处理不到第二个<name>。
解决方案
把匹配<names>的模板改成遍历每个<name>元素,在遍历过程中判断是否是第一个元素,只在第一行显示“Name”,后续行的第一列留空即可。修改后的模板代码如下:
<xsl:template match="names"> <!-- 遍历每个name元素 --> <xsl:for-each select="name"> <fo:table-row> <fo:table-cell column-number="1" border="1pt solid black"> <fo:block> <!-- 只有第一个name元素显示"Name",其余留空 --> <xsl:if test="position() = 1">Name</xsl:if> </fo:block> </fo:table-cell> <fo:table-cell column-number="2" border="1pt solid black"> <fo:block> <xsl:value-of select="@language"/> </fo:block> </fo:table-cell> <fo:table-cell column-number="3" border="1pt solid black"> <fo:block> <xsl:value-of select="."/> </fo:block> </fo:table-cell> </fo:table-row> </xsl:for-each> </xsl:template>
这样修改后,每个<name>元素都会生成一行表格:第一行第一列显示“Name”,第二行第一列空着,两行分别展示DE和EN的语言标签及对应名称,完美解决你之前的问题。另外注意你原来的XSLT代码末尾有截断,记得把模板标签都闭合完整哦。
内容的提问来源于stack exchange,提问作者Christoph
相关产品推荐
相关产品推荐

