【问题标题】:Writing Columns of data versus Rows in Excel from XML with XSLT使用 XSLT 从 XML 中写入数据列与 Excel 中的行
【发布时间】:2015-04-23 16:42:15
【问题描述】:

我正在尝试采用下面的 XML 结构(已清理,因此请原谅任何不一致之处)并通过 XSLT 1.0 将其转换为每个频道两列的分组列,一列用于开始时间,另一列用于节目标题。 XML 看起来像:

<days>
<DAY>
    <channel>
        <CHANNEL name="A"/>
    </channel>
    <transmissions>
        <TRANSMISSION title="Show 1">
            <starttime>
                <TIME hours="6" minutes="00" seconds="00" timeinseconds="21600"/>
            </starttime>
        </TRANSMISSION>
        <TRANSMISSION title="Show 2">
            <starttime>
                <TIME hours="7" minutes="45" seconds="00" timeinseconds="27900"/>
            </starttime>
        </TRANSMISSION>
        <TRANSMISSION title="Show 3">
            <starttime>
                <TIME hours="8" minutes="00" seconds="00" timeinseconds="28800"/>
            </starttime>
        </TRANSMISSION>
    </transmissions>
    <date>
        <DATE year="2015" month="3" day="2" dayname="Monday" monthname="March" dateindays="41698"/>
    </date>
</DAY>
<DAY>
    <channel>
        <CHANNEL name="B"/>
    </channel>
    <transmissions>
        <TRANSMISSION title="Show 1">
            <starttime>
                <TIME hours="6" minutes="00" seconds="00" timeinseconds="21600"/>
            </starttime>
        </TRANSMISSION>
        <TRANSMISSION title="Show 4">
            <starttime>
                <TIME hours="7" minutes="45" seconds="00" timeinseconds="27900"/>
            </starttime>
        </TRANSMISSION>
        <TRANSMISSION title="Show 2">
            <starttime>
                <TIME hours="8" minutes="00" seconds="00" timeinseconds="28800"/>
            </starttime>
        </TRANSMISSION>
    </transmissions>
    <date>
        <DATE year="2015" month="3" day="2" dayname="Monday" monthname="March" dateindays="41698"/>
    </date>
</DAY>
</days>

从这段代码中,我需要创建一个 Excel 文档,每个通道有两列,一列是开始时间,另一列是传输的标题。这些需要并排(我会发布图片,但我没有足够的声誉)。

我已尝试针对 TRANSMISSION 构建一个 for-each,但这当然意味着第二个通道将被忽略,直到第一个通道传输完成,这会在通道 A 的第 2 行和通道 B 的第 3 行创建数据的数据在两个通道的第 2 行中。

<xsl:for-each select="DAY/transmissions/TRANSMISSION">
    <xsl:sort select="starttime/TIME/@durationinseconds" data-type="number" order="ascending"/>
        <Row ss:AutoFitHeight="0" ss:Height="29.25">
            <xsl:if test="../../channel/CHANNEL/@name = 'A'">
                <xsl:variable name="time">
                    <xsl:value-of select="starttime/TIME/@timeinseconds"/>
                </xsl:variable>
                <Cell>
                    <Data ss:Type="String">
                        <xsl:apply-templates select="starttime/TIME" mode="time"/>
                    </Data>
                </Cell>
                <Cell ss:StyleID="s17">
                    <Data ss:Type="String">
                        <xsl:apply-templates select="../TRANSMISSION[starttime/TIME/@timeinseconds = $time]"/>
                    </Data>
                </Cell>
            </xsl:if>
            <xsl:if test="../../channel/CHANNEL/@name = 'B'"> (repeat of above)

我意识到这段代码有多个问题,但核心问题是我在处理这段代码中的其他较小问题之前试图解决的问题。

实际上我需要一次写入一列而不是一行,但这当然是不可能的。如何让数据显示为两个分组的数据列,其中每个频道都有一个开始时间列表和相关的节目标题?

我考虑过创建一个“主”表,然后使用参考公式来提取信息,但是完整报告将提取的每个通道的数据量和数据的可变性会导致如何构建“子表”的问题“床单。更不用说最终用户不喜欢这个解决方案。

我也尝试过针对 DAY 节点构建 for-each,但在切换到通道 B 之前构建整个通道 A 数据会加剧问题。

【问题讨论】:

    标签: xml excel xslt xslt-1.0


    【解决方案1】:

    此样式表可以作为您尝试做的事情的起点:

    XSLT 1.0

    <!-- (I needed a starting point, you will probably have something different) -->
    <xsl:template match="/">
        <wrapperElement>
            <!-- create the rows -->
            <xsl:apply-templates select="//DAY[1]//TRANSMISSION" mode="createRow"/>
        </wrapperElement>
    </xsl:template>
    
    <!-- template to create the row -->
    <xsl:template match="TRANSMISSION" mode="createRow">
        <Row ss:AutoFitHeight="0" ss:Height="29.25">
            <xsl:apply-templates select="." mode="createCells"/>
        </Row>
    </xsl:template>
    
    <!-- template to create two cells for each channel -->
    <xsl:template match="TRANSMISSION" mode="createCells">
        <Cell>
            <Data ss:Type="String">
                <xsl:apply-templates select="starttime/TIME" mode="time"/>
            </Data>
        </Cell>
        <Cell ss:StyleID="s17">
            <Data ss:Type="String">
                <xsl:apply-templates select="@title"/>
            </Data>
        </Cell>
        <!-- cells for next channel -->
        <xsl:apply-templates select="ancestor::DAY/following-sibling::DAY[1]//TRANSMISSION[count(preceding-sibling::TRANSMISSION) = count(current()/preceding-sibling::TRANSMISSION)]" mode="createCells"/>
    </xsl:template>
    

    值得注意的点:

    • 第一个频道的每个TRANSMISSION元素都被匹配以创建一个Row
    • 相同的元素由另一个模板(在不同的mode)处理以生成两个Cells 和时间和标题
    • 然后处理下一个频道相同位置TRANSMISSION元素,在同一个Row中创建其他Cells

    【讨论】:

    • @JasonH 在将模板应用到 下一个频道 中相应的 TRANSMISSION 元素时,我修复了最后一行中的一个错误(而不是所有以下频道,如果频道超过 2 个,则会产生重复);如果您仍然在输出中看到有问题,请考虑在问题中添加一个更大的示例,以便我们可以重现该问题。
    • @JasonH 澄清一下:请注意,目前样式表没有考虑不同传输的开始时间;它只是假设每个通道的传输是有序的,并将每个通道的第一次传输放在同一行,然后为每个通道的第二次传输创建另一行等。
    • @Ifurini 我可以看到这个工作。现在我必须修复其余的错误代码,以确保获得所需的结果。但是这个模板结构成功了。谢谢!
    猜你喜欢
    • 2016-01-30
    • 2014-08-17
    • 1970-01-01
    • 1970-01-01
    • 2019-09-12
    • 1970-01-01
    • 2013-08-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多