xml - XSL : create a cross tab under Excel with XSLT

标签 xml xslt spreadsheetml

我有一个像这样的 xml 文件:

<products>
    <cars>
        <car>
            <brand>Audi</brand>
            <type>S3</type>
            <attribs>
                <attrib>
                    <color>1</color>
                    <price>45000</price>
                </attrib>
                <attrib>
                    <color>2</color>
                    <price>75000</price>
                </attrib>
                <attrib>
                    <color>4</color>
                    <price>35000</price>
                </attrib>
            </attribs>      
        </car>

        <!-- Many cars following -->

    </cars>
    <colors>
        <color>
            <id>1</id>
            <shortdesc>Blue</shortdesc>
            <description>Blue Lagoon</description>
        </color>
        <color>
            <id>2</id>
            <shortdesc>Red</shortdesc>
            <description>Red Sport</description>
        </color>
        <color>
            <id>3</id>
            <shortdesc>Green</shortdesc>
            <description>Green Forest</description>
        </color>
        <color>
            <id>4</id>
            <shortdesc>Yellow</shortdesc>
            <description>Yellow</description>
        </color>
                <!-- many colors -->
    </colors>
</products>

我正在一个 excel 交叉表中转换这个 XML,其中我有垂直的汽车和水平的颜色,如下所示:
Brand / Model       1/Blue   2/Red   3/Green   4/Yellow

Audi S3             45000    75000   -         35000    
Audi S6             66000    68000   59000     -
Jaguar x-type       98000    -       99500     -

并且 xslt 看起来像:
<ss:Table>
    <ss:Row>
        <!-- This is the header row -->
        <ss:Cell>
            <ss:Data ss:Type="String">Brand / Model</ss:Data>
        </ss:Cell>
        <xsl:for-each select="colors/color">
            <ss:Cell>
                <ss:Data ss:Type="String">
                    <xsl:value-of select="id"/>/<xsl:value-of select="shortdesc"/>
                </ss:Data>
            </ss:Cell>
        </xsl:for-each>
    </ss:Row>
    <xsl:for-each select="cars/car">
        <ss:Row>
            <ss:Cell>
                <ss:Data ss:Type="String">
                    <xsl:value-of select="brand"/> <xsl:value-of select="type"/>
                </ss:Data>
            </ss:Cell>
            <xsl:for-each select="attribs/attrib">
                <!-- I Know this is incorrect, but what to put in the if ? -->
                <xsl:if test="color = /colors/color/id">
                    <ss:Cell>
                        <ss:Data ss:Type="String">
                            <xsl:value-of select="price"/>
                        </ss:Data>
                    </ss:Cell>
                </xsl:if>
                <xsl:if test="color != /colors/color/id">
                    <ss:Cell>
                        <ss:Data ss:Type="String">-</ss:Data>
                    </ss:Cell>
                </xsl:if>
            </xsl:for-each>
        </ss:Row>
    </xsl:for-each>
</ss:Table>

我正在寻找在右栏中比较/写入价格的方法。

该模板引用节点/products 以便能够访问汽车和颜色。
一种可能的方法是在我在标题中写入颜色的同时创建一个数组,并在我处理汽车时与它进行比较;
这是一种可能的方法还是可能有更好的方法?

另一个细节:我无法更改 XML,因为它已经是这样设计的(实际上它不是我正在处理的汽车,只是为了让它更简单)

最佳答案

您可以使用复合 key 按品牌、类型和颜色收集汽车:

<xsl:output indent="yes"/>
<xsl:key name="k_cars" 
    match="/products/cars/car/attribs/attrib" 
    use="concat(../../brand,../../type,color)"/>

然后迭代 cars/car并且,因为您需要检查所有颜色的价格(有些汽车可能会漏掉一种颜色),请在 colors/color 上进行子迭代并使用 xsl:choose对 key 进行测试。如果key返回一个节点,则打印对应的价格;否则打印 - :
<xsl:variable name="car" select="."/>

<xsl:for-each select="/products/colors/*">

    <xsl:variable name="v_CarColor" 
        select="key('k_cars',concat($car/brand,$car/type,id))"/>

    <xsl:choose>

        <xsl:when test="$v_CarColor">
            <ss:Cell>
                <ss:Data ss:Type="String">
                    <xsl:value-of select="$v_CarColor/price"/>
                </ss:Data>
            </ss:Cell>
        </xsl:when>

        <xsl:otherwise>
            <ss:Cell>
                <ss:Data ss:Type="String">-</ss:Data>
            </ss:Cell>
        </xsl:otherwise>

    </xsl:choose>
</xsl:for-each>

您的最终转换:
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ss="excel.xml">

    <xsl:output indent="yes"/>
    <xsl:key name="k_cars" 
        match="/products/cars/car/attribs/attrib" 
        use="concat(../../brand,../../type,color)"/>

    <xsl:template match="/products">
        <ss:Table>
            <ss:Row>
                <!-- This is the header row -->
                <ss:Cell>
                    <ss:Data ss:Type="String">Brand / Model</ss:Data>
                </ss:Cell>
                <xsl:for-each select="colors/color">
                    <ss:Cell>
                        <ss:Data ss:Type="String">
                            <xsl:value-of select="id"/>/<xsl:value-of select="shortdesc"/>
                        </ss:Data>
                    </ss:Cell>
                </xsl:for-each>
            </ss:Row>

            <xsl:for-each select="cars/car">

                <ss:Row>
                    <ss:Cell>
                        <ss:Data ss:Type="String">
                            <xsl:value-of select="brand"/> <xsl:value-of select="type"/>
                        </ss:Data>
                    </ss:Cell>


                    <xsl:variable name="car" select="."/>

                    <xsl:for-each select="/products/colors/*">

                        <xsl:variable name="v_CarColor" 
                            select="key('k_cars',concat($car/brand,$car/type,id))"/>

                        <xsl:choose>

                            <xsl:when test="$v_CarColor">
                                <ss:Cell>
                                    <ss:Data ss:Type="String">
                                        <xsl:value-of select="$v_CarColor/price"/>
                                    </ss:Data>
                                </ss:Cell>
                            </xsl:when>

                            <xsl:otherwise>
                                <ss:Cell>
                                    <ss:Data ss:Type="String">-</ss:Data>
                                </ss:Cell>
                            </xsl:otherwise>

                        </xsl:choose>
                    </xsl:for-each>
                </ss:Row>
            </xsl:for-each>
        </ss:Table> 
    </xsl:template>

</xsl:stylesheet>

关于xml - XSL : create a cross tab under Excel with XSLT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6958734/

相关文章:

c# - 反序列化异常处理

xml - - <?xml version ="1.0"encoding ="utf-8"?> 的含义

xslt - 如何在xslt中的数据之间应用空间

xslt - 从 Sitecore 中的 Xslt 辅助函数类修改页面控件

xslt - 在 XSL 中组合两个节点集

c# - 如何使用 OpenXml 将大纲表格边框应用于单元格范围?

xml - 验证 XML/识别中断点的快速方法

php - 意外的 T_String 错误和 rss