Excel电子表格使用XML和XSLTdynamic单元格着色
我得到了一个XML源文件(来自其他来源)及其各自的XSL文件,以将XML转换为Excel电子表格。
我需要的是根据XML属性为使用XSL的节点dynamic更改电子表格中单元格的背景颜色
例如: Source.XML内容
<workbooks> <Wrkbook <table Id="My table 1"> <Colnames> <ColumnName>Student ID</ColumnName> <ColumnName>Student Name</ColumnName> <ColumnName>Subject 1</ColumnName> <ColumnName>Subject 2</ColumnName> <ColumnName>Subject 3</ColumnName> <ColumnName>Subject 4</ColumnName> <ColumnName>Subject 5</ColumnName> <ColumnName>Subject 6</ColumnName> </Colnames> <Rows> <CI>534</CI> <CI>Ramu</CI> <CI>67</CI> <CI Colour="Green">67</CI> <CI Colour="#e8e9e8">48</CI> <CI>66</CI> <CI Colour="#B3C389">39</CI> <CI>67</CI> </Rows> <Rows> <CI>534</CI> <CI>Raul</CI> <CI Colour="Green">63</CI> <CI>89</CI> <CI Colour="#007788">67</CI> <CI>57</CI> <CI>75</CI> <CI Colour="#AABBCC">92</CI> </Rows> </table> </Wrkbook> </workbooks>
我之前做的事情是,我曾经在XSL表格中为其各自的XML标签中的属性设置了绿色和Yelow的特定样式,因此我可以pipe理它。 但现在源文件包含颜色代码,我需要基于颜色代码来突出显示。 XSL文件:
<xsl:template match='/'> <Workbook> <Styles> <Style ss:ID="Green"> <Borders> <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/> <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/> <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/> <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/> </Borders> <Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="11"/> <Interior ss:Color="#CCFFCC" ss:Pattern="Solid"/> </Style> </Styles> <xsl:for-each select='workbooks'> <xsl:for-each select='Wrkbook'> <Worksheet ss:Name='Scores'> <Table x:FullColumns='1' x:FullRows='1'> <xsl:for-each select='table'> <Row> <xsl:for-each select='Colnames/ColumnName'> <Cell ss:StyleID='s41'> <Data ss:Type='String'> <xsl:value-of select='.' disable-output-escaping='yes'/> </Data> </Cell> </xsl:for-each> </Row> <xsl:for-each select='Rows'> <Row> <xsl:for-each select='CI'> <xsl:choose> <xsl:when test="@Colour = 'Green'"> <Cell ss:StyleID='Green'> <Data ss:Type='String'> <xsl:value-of select='.' disable-output-escaping='yes'/> </Data> </Cell> </xsl:when> <xsl:otherwise> <Cell ss:StyleID='s38'> <Data ss:Type='String'> <xsl:value-of select='.' disable-output-escaping='yes'/> </Data> </Cell> </xsl:otherwise> </xsl:choose> </xsl:for-each> </Row> </xsl:for-each> <Row> <Cell></Cell> </Row> </xsl:for-each> </Table> </Worksheet> </xsl:for-each> </xsl:for-each> </Workbook> </xsl:template>
请忽略XSL中是否有语法错误,只是我正在寻找基于dynamicXML属性高亮显示电子表格颜色。
谢谢!!
使用单个模板而不是嵌套for-each
元素要容易得多 。 所以我build议重新组织整个样式表,这样每个模板就可以处理一部分源代码树。 另外,由于必须生成不遵循源树结构的附加数据,因此可以调用具有命名的模板。 我把这个答案分成两部分。 第二部分将试图回答你的问题。
第1部分:重构样式表
在匹配根( /
)的模板中,您只需要放置文件的不变基本结构 。 在它里面你可以使用<apply-templates select="*/*/table"/>
所以select与你的表中的数据相匹配的模板(跳过workbooks
和Wrkbook
)。 由于标题涉及基于颜色的样式的创build,我们稍后将处理它。 所需的<?mso-application progid="Excel.Sheet" ?>
处理指令也会生成:
<xsl:template match='/'> <xsl:processing-instruction name="mso-application">progid="Excel.Sheet"</xsl:processing-instruction> <Workbook> <Styles> <!-- make Style elements for each color here --> </Styles> <Worksheet ss:Name='Scores'> <Table x:FullColumns='1' x:FullRows='1'> <xsl:apply-templates select="*/*/table"/> <!-- will process the rest of the tree --> </Table> </Worksheet> </Workbook> </xsl:template>
table
的模板也很简单。 根据您提供的样式表,它包含一个<Row>
,其中包含来自源XML中Colnames/ColumnName
元素的数据,每个<Rows>
元素包含若干<Row>
元素,以及包含空的<Cell>
。 这可以像这样重写:
<xsl:template match="table"> <Row><xsl:apply-templates select='Colnames/ColumnName'/></Row> <xsl:apply-templates select='Rows'/> <Row><Cell/></Row> </xsl:template>
有两个 <xsl:apply-templates/>
。 第一个将调用模板来构build包含多个ColumnName
第一个<Row>
。 第二个将build立单独的数据行。 这是将为每个ColumnName
调用的模板:
<xsl:template match="ColumnName"> <Cell ss:StyleID='s41'> <Data ss:Type='String'> <xsl:value-of select='.' disable-output-escaping='yes'/> </Data> </Cell> </xsl:template>
这一行将被要求每Rows
:
<xsl:template match="Rows"> <Row><xsl:apply-templates select='CI'/></Row> </xsl:template>
再一次<xsl:aply-templates/>
。 这个将处理每个包含Cell
数据的CI
元素。 它将不得不从每个元素读取Colour
属性,并决定如何渲染它。 目前你只需要区分Green
和什么都没有。 我们可以将其改写为:
<xsl:template match="CI"> <xsl:choose> <xsl:when test="@Colour = 'Green'"> <Cell ss:StyleID='Green'> <xsl:call-template name="print-cell"/> </Cell> </xsl:when> <xsl:otherwise> <Cell ss:StyleID='s38'> <xsl:call-template name="print-cell"/> </Cell> </xsl:otherwise> </xsl:choose> </xsl:template>
删除重复,并将单元格打印到一个命名模板中,我们使用<xsl:call-template>
:
<xsl:template name="print-cell"> <Data ss:Type='String'> <xsl:value-of select='.' disable-output-escaping='yes'/> </Data> </xsl:template>
第2部分:添加颜色信息
现在样式表被重新组织在较小的模板中,我们可以继续调整它,以便正确读取颜色信息。
首先我们应该制作一个包含所有可用颜色的key
:
<xsl:key name="colours" match="CI/@Colour" use="."/>
这将匹配所有CI
元素中的所有Colour
属性。 我们可以使用 Colour
属性的内容来select它。
在根模板中,我们可以通过为每种颜色构build一个Style
元素来构build<Styles>
块。 我们将在一个单独的模板中这样做,但首先我们需要在文档中的独特颜色之间进行迭代。 这可以通过在XSLT 1.0中使用Muenchian分组技术来完成(如果您使用的是XSLT 2.0,则可以使用for-each-group
以更简单的方式处理此问题):
<Styles> <xsl:for-each select="//CI/@Colour[count(. | key('colours', .)[1]) = 1]"> <xsl:call-template name="make-style"> <xsl:with-param name="colour" select="."/> </xsl:call-template> </xsl:for-each> </Styles>
这将调用在源文档中find的每种独特颜色的make-style
模板。 使用包含当前颜色的参数colour
调用模板。
我们需要每种颜色的ID。 可以使用颜色名称,但ID不能以散列值开始,所以解决问题的一种方法是使用移除#
的颜色代码。 您可以使用XPath translate()
函数: translate($colour,'#','')
。
由于您的源数据仍然使用Green
,并且不符合该模式,所以我们必须单独处理。 要设置Style
ss:ID
属性,我们需要使用原始属性的内容(如果$colour
是Green
,否则去掉#
:
<xsl:choose> <xsl:when test="$colour = 'Green'"> <xsl:value-of select="$colour"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="translate($colour,'#','')"/> </xsl:otherwise> </xsl:choose>
我们也必须这样做,build立Interior
元素:
<xsl:choose> <xsl:when test="$colour = 'Green'"> <Interior ss:Color="#CCFFCC" ss:Pattern="Solid"/> </xsl:when> <xsl:otherwise> <Interior ss:Color="{$colour}" ss:Pattern="Solid"/> </xsl:otherwise> </xsl:choose>
这是最终的make-style
模板:
<xsl:template name="make-style"> <xsl:param name="colour"/> <Style ss:ID="Green"> <xsl:attribute name="ss:ID"> <xsl:choose> <xsl:when test="$colour = 'Green'"> <xsl:value-of select="$colour"></xsl:value-of> </xsl:when> <xsl:otherwise> <xsl:value-of select="translate($colour,'#','')"/> </xsl:otherwise> </xsl:choose> </xsl:attribute> <Borders> <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/> <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/> <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/> <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/> </Borders> <Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="11"/> <xsl:choose> <xsl:when test="$colour = 'Green'"> <Interior ss:Color="#CCFFCC" ss:Pattern="Solid"/> </xsl:when> <xsl:otherwise> <Interior ss:Color="{$colour}" ss:Pattern="Solid"/> </xsl:otherwise> </xsl:choose> </Style> </xsl:template>
现在我们可以重构CI
模板,添加一个xsl:when
块来生成一个StyleID
为合适颜色的单元格:
<xsl:template match="CI"> <xsl:choose> <xsl:when test="@Colour = 'Green'"> <Cell ss:StyleID='Green'> <xsl:call-template name="print-cell"/> </Cell> </xsl:when> <xsl:when test="starts-with(@Colour, '#')"> <Cell ss:StyleID="{translate(@Colour,'#','')}"> <xsl:call-template name="print-cell"/> </Cell> </xsl:when> <xsl:otherwise> <Cell ss:StyleID='s38'> <xsl:call-template name="print-cell"/> </Cell> </xsl:otherwise> </xsl:choose> </xsl:template>
这是最终的样式表。
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" version="1.0"> <xsl:output indent="yes"/> <xsl:key name="colours" match="CI/@Colour" use="."/> <xsl:template match='/'> <xsl:processing-instruction name="mso-application">progid="Excel.Sheet"</xsl:processing-instruction> <Workbook> <Styles> <xsl:for-each select="//CI/@Colour[count(. | key('colours', .)[1]) = 1]"> <xsl:call-template name="make-style"> <xsl:with-param name="colour" select="."/> </xsl:call-template> </xsl:for-each> </Styles> <Worksheet ss:Name='Scores'> <Table x:FullColumns='1' x:FullRows='1'> <xsl:apply-templates select="*/*/table"/> </Table> </Worksheet> </Workbook> </xsl:template> <xsl:template match="table"> <Row><xsl:apply-templates select='Colnames/ColumnName'/></Row> <xsl:apply-templates select='Rows'/> <Row><Cell/></Row> </xsl:template> <xsl:template match="ColumnName"> <Cell ss:StyleID='s41'> <Data ss:Type='String'> <xsl:value-of select='.' disable-output-escaping='yes'/> </Data> </Cell> </xsl:template> <xsl:template match="Rows"> <Row><xsl:apply-templates select='CI'/></Row> </xsl:template> <xsl:template name="make-style"> <xsl:param name="colour"/> <Style ss:ID="Green"> <xsl:attribute name="ss:ID"> <xsl:choose> <xsl:when test="$colour = 'Green'"> <xsl:value-of select="$colour"></xsl:value-of> </xsl:when> <xsl:otherwise> <xsl:value-of select="translate($colour,'#','')"/> </xsl:otherwise> </xsl:choose> </xsl:attribute> <Borders> <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/> <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/> <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/> <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/> </Borders> <Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="11"/> <xsl:choose> <xsl:when test="$colour = 'Green'"> <Interior ss:Color="#CCFFCC" ss:Pattern="Solid"/> </xsl:when> <xsl:otherwise> <Interior ss:Color="{$colour}" ss:Pattern="Solid"/> </xsl:otherwise> </xsl:choose> </Style> </xsl:template> <xsl:template match="CI"> <xsl:choose> <xsl:when test="@Colour = 'Green'"> <Cell ss:StyleID='Green'> <xsl:call-template name="print-cell"/> </Cell> </xsl:when> <xsl:when test="starts-with(@Colour, '#')"> <Cell ss:StyleID="{translate(@Colour,'#','')}"> <xsl:call-template name="print-cell"/> </Cell> </xsl:when> <xsl:otherwise> <Cell ss:StyleID='s38'> <xsl:call-template name="print-cell"/> </Cell> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="print-cell"> <Data ss:Type='String'> <xsl:value-of select='.' disable-output-escaping='yes'/> </Data> </xsl:template> </xsl:stylesheet>
您可以看到结果并在此XSLT小提琴中进行调整。