格式化表列以忽略重复的XSLT-1.0

时间:2015-06-09 20:12:36

标签: xslt xslt-1.0 xsl-fo

我希望有人可以用XSLT 1.0中的概念指出我正确的方向。我正在生成一个PDF,因此代码有点长,我认为仅包含相关位更合适。

我有类似于以下框架的XML(完整的XML有几十行,每行包含的不仅仅是生产者和出版物):

<root>
    <table>
        <row>
            <PRODUCER/>
            <PUBLICATION_CODE_-_NAME/>
        </row>
        <row>
            <PRODUCER/>
            <PUBLICATION_CODE_-_NAME/>
        </row>
    </table>
</root>

我目前能够使用XSLT生成一个类似于包含多行的表:

|  Producer Name  |  Publication  |
----------------------------------            
|Producer 1       |Publication A  |
|Producer 1       |Publication B  |
|Producer 1       |Publication C  |
|Producer 2       |Publication D  |
|Producer 2       |Publication E  |
|Producer 2       |Publication F  |

以这种方式等等。

在我的XSLT中生成它的代码的主要部分是:

<xsl:template match ="table">
    <fo:table>
        <fo:table-body  font-size="10pt"
                        font-family="sans-serif"
                        line-height="10pt"
                        space-after.optimum="3pt">
            <xsl:for-each select="row">
                <fo:table-row>
                    <fo:table-cell  width="2.125in"
                                    height="0.2in">
                        <xsl:choose>
                        <xsl:apply-templates select="PRODUCER"/>
                        </xsl:choose>
                    </fo:table-cell>
                    <fo:table-cell  width="3.25in"
                                    height="0.2in">
                        <xsl:apply-templates select="PUBLICATION_CODE_-_NAME"/>
                    </fo:table-cell>
                </fo:table-row>
            </xsl:for-each>
        </fo:table-body>
    </fo:table>
</xsl:template>

<xsl:template match="PRODUCER">
    <fo:block>
            <xsl:value-of select="@value"/>
    </fo:block>
</xsl:template>

<xsl:template match="PUBLICATION_CODE_-_NAME">
    <fo:block>
        <xsl:value-of select="@value"/>
    </fo:block>
</xsl:template>

现在出现的问题是我希望表格的输出看起来更像这样,而不是上表。

|  Producer Name  |  Publication  |
----------------------------------            
|Producer 1       |Publication A  |
|                 |Publication B  |
|                 |Publication C  |
|Producer 2       |Publication D  |
|                 |Publication E  |
|                 |Publication F  |

我试图这样做的方式是在XSLT的这一部分

<xsl:template match="PRODUCER">
    <fo:block>
            <xsl:value-of select="@value"/>
    </fo:block>
</xsl:template>

此时,据我所知,上下文节点是PRODUCER。因此,为了比较之前行中的PRODUCER值,我需要使用某些东西来实现../preceding-sibling的效果,以便找到最后一行(而不是PRODUCER)的前一个兄弟。此外,生产者已经订购了这些信息,因此我只需要查看最接近的兄弟而不是所有这些信息。

我试图用来解决此问题的代码如下:

<xsl:template match="PRODUCER">
    <fo:block>
        <xsl:if test="not(../preceding-sibling::PRODUCER/@value = self/@value>
            <xsl:value-of select="@value"/>
        </xsl:if>
    </fo:block>
</xsl:template>

我不知道语法是否不正确,或者这是否是一个好方法,但是任何和所有输入都将非常感激。如果我能提供的任何其他信息有助于澄清,或者如果我的问题有任何问题,请告诉我。

谢谢

1 个答案:

答案 0 :(得分:2)

可以通过分组完成,也可以使用previous-sibling完成。我会注意到你说“几十个......”我不担心使用分组。我用2000行运行这个样本,执行时间是2.3秒(在使用Saxon的oXygen中的调试模式下),0.1秒没有调试。几十个人只需要几分之一秒。我已经在这里扩展了你的样本,包括第一次出现没有先前兄弟的测试。你也可以把事情结合起来,但我离开了这个,所以你可以看到不同的决定:

示例XML:

<root>
    <table>
        <row>
            <PRODUCER>1</PRODUCER>
            <PUBLICATION>A</PUBLICATION>
        </row>
        <row>
            <PRODUCER>1</PRODUCER>
            <PUBLICATION>B</PUBLICATION>
        </row>
        <row>
            <PRODUCER>1</PRODUCER>
            <PUBLICATION>C</PUBLICATION>
        </row>
        <row>
            <PRODUCER>2</PRODUCER>
            <PUBLICATION>B</PUBLICATION>
        </row>
        <row>
            <PRODUCER>2</PRODUCER>
            <PUBLICATION>C</PUBLICATION>
        </row>
        <row>
            <PRODUCER>3</PRODUCER>
            <PUBLICATION>A</PUBLICATION>
        </row>
        <row>
            <PRODUCER>4</PRODUCER>
            <PUBLICATION>B</PUBLICATION>
        </row>
    </table>
</root>

XSL测试前兄弟,放入变量使用,只选择前一个变量[1]:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"
    version="1.0">
    <xsl:output indent="yes"/>
    <xsl:template match ="table">
        <fo:table>
            <fo:table-body  font-size="10pt"
                font-family="sans-serif"
                line-height="10pt"
                space-after.optimum="3pt">
                <xsl:for-each select="row">
                    <fo:table-row>
                        <fo:table-cell  width="2.125in"
                            height="0.2in">
                                <xsl:apply-templates select="PRODUCER"/>

                        </fo:table-cell>
                        <fo:table-cell  width="3.25in"
                            height="0.2in">
                            <xsl:apply-templates select="PUBLICATION"/>
                        </fo:table-cell>
                    </fo:table-row>
                </xsl:for-each>
            </fo:table-body>
        </fo:table>
    </xsl:template>

        <xsl:template match="PRODUCER">
    <fo:block>
        <!-- Get the previous row element to the one I am in -->
        <xsl:variable name="test" select="parent::row/preceding-sibling::row[1]"/>
        <xsl:choose>
            <!-- First test, do we have a row? -->
            <xsl:when test="$test">
                <!-- Yes we have a previous row -->
                <xsl:choose>
                    <!-- Next, is the previous row's PRODUCER text the same as ours? -->
                    <xsl:when test="$test/PRODUCER/text() = text()">
                        <!-- It is, output nothing -->
                        <fo:leader/>
                    </xsl:when>
                    <xsl:otherwise>
                        <!-- It is not, so output it -->
                        <xsl:value-of select="concat('Producer ',.)"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <!-- We are the first row (no previous one) so output it  -->
            <xsl:otherwise>
                <xsl:value-of select="concat('Producer ',.)"/>
            </xsl:otherwise>
        </xsl:choose>
    </fo:block>
</xsl:template>

    <xsl:template match="PUBLICATION">
        <fo:block>
            <xsl:value-of select="concat('Publication ',.)"/>
        </fo:block>
    </xsl:template>
</xsl:stylesheet>

输出:

<fo:table xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:table-body font-size="10pt" font-family="sans-serif" line-height="10pt" space-after.optimum="3pt">
  <fo:table-row>
     <fo:table-cell width="2.125in" height="0.2in">
        <fo:block>Producer 1</fo:block>
     </fo:table-cell>
     <fo:table-cell width="3.25in" height="0.2in">
        <fo:block>Publication A</fo:block>
     </fo:table-cell>
  </fo:table-row>
  <fo:table-row>
     <fo:table-cell width="2.125in" height="0.2in">
        <fo:block>
           <fo:leader/>
        </fo:block>
     </fo:table-cell>
     <fo:table-cell width="3.25in" height="0.2in">
        <fo:block>Publication B</fo:block>
     </fo:table-cell>
  </fo:table-row>
  <fo:table-row>
     <fo:table-cell width="2.125in" height="0.2in">
        <fo:block>
           <fo:leader/>
        </fo:block>
     </fo:table-cell>
     <fo:table-cell width="3.25in" height="0.2in">
        <fo:block>Publication C</fo:block>
     </fo:table-cell>
  </fo:table-row>
  <fo:table-row>
     <fo:table-cell width="2.125in" height="0.2in">
        <fo:block>Producer 2</fo:block>
     </fo:table-cell>
     <fo:table-cell width="3.25in" height="0.2in">
        <fo:block>Publication B</fo:block>
     </fo:table-cell>
  </fo:table-row>
  <fo:table-row>
     <fo:table-cell width="2.125in" height="0.2in">
        <fo:block>
           <fo:leader/>
        </fo:block>
     </fo:table-cell>
     <fo:table-cell width="3.25in" height="0.2in">
        <fo:block>Publication C</fo:block>
     </fo:table-cell>
  </fo:table-row>
  <fo:table-row>
     <fo:table-cell width="2.125in" height="0.2in">
        <fo:block>Producer 3</fo:block>
     </fo:table-cell>
     <fo:table-cell width="3.25in" height="0.2in">
        <fo:block>Publication A</fo:block>
     </fo:table-cell>
  </fo:table-row>
  <fo:table-row>
     <fo:table-cell width="2.125in" height="0.2in">
        <fo:block>Producer 4</fo:block>
     </fo:table-cell>
     <fo:table-cell width="3.25in" height="0.2in">
        <fo:block>Publication B</fo:block>
     </fo:table-cell>
  </fo:table-row>
</fo:table-body>
</fo:table>