使用 XSLT 从 XML 写入数据列与行 Excel
Writing Columns of data versus Rows in Excel from XML with XSLT
我正在尝试采用下面的 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 文档,每个频道有两列,一列是开始时间,另一列是传输的标题。这些必须是 side-by-side(我会 post 图片,但我没有足够的声誉)。
我已经尝试针对传输构建一个 for-each,但这当然意味着第二个通道将被忽略,直到第一个通道传输完成,这会在第 2 行中为通道 A 和第 3 行创建数据对于通道 B,而不是两个通道的数据都在第 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)
我意识到这段代码有多个问题,但核心问题是我在处理这段代码中的其他小问题之前试图解决的问题。
实际上我需要一次写一列而不是一行,但这当然是不可能的。如何让数据显示为两组数据列,其中每个频道都有一个开始时间列表和相关的节目标题?
我考虑过创建一个 "master" sheet 然后使用参考公式来拉取信息,但是完整报告将拉取的数据量和每个通道的数据可变性创建有关如何构建 "child" sheet 的问题。更不用说最终用户不喜欢这个解决方案了。
我也尝试过针对 DAY 节点构建 for-each,但是在切换到通道 B 之前构建整个通道 A 数据会加剧问题。
此样式表可以作为您尝试执行的操作的起点:
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
中)处理以生成带有时间和标题的两个 Cell
s
- 然后处理下一个通道具有相同位置的
TRANSMISSION
元素以在相同Row
中创建其他Cell
s
我正在尝试采用下面的 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 文档,每个频道有两列,一列是开始时间,另一列是传输的标题。这些必须是 side-by-side(我会 post 图片,但我没有足够的声誉)。
我已经尝试针对传输构建一个 for-each,但这当然意味着第二个通道将被忽略,直到第一个通道传输完成,这会在第 2 行中为通道 A 和第 3 行创建数据对于通道 B,而不是两个通道的数据都在第 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)
我意识到这段代码有多个问题,但核心问题是我在处理这段代码中的其他小问题之前试图解决的问题。
实际上我需要一次写一列而不是一行,但这当然是不可能的。如何让数据显示为两组数据列,其中每个频道都有一个开始时间列表和相关的节目标题?
我考虑过创建一个 "master" sheet 然后使用参考公式来拉取信息,但是完整报告将拉取的数据量和每个通道的数据可变性创建有关如何构建 "child" sheet 的问题。更不用说最终用户不喜欢这个解决方案了。
我也尝试过针对 DAY 节点构建 for-each,但是在切换到通道 B 之前构建整个通道 A 数据会加剧问题。
此样式表可以作为您尝试执行的操作的起点:
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
中)处理以生成带有时间和标题的两个Cell
s - 然后处理下一个通道具有相同位置的
TRANSMISSION
元素以在相同Row
中创建其他Cell
s