0

次のような XML ファイルを転置する必要があります。

<?xml version="1.0" encoding="UTF-8"?>
<products>
    <product id="10" name="Widget 1" price="15.99" category_code="T" category="Toys" manufacturer_code="SC1" manufacturer="Some Company 1" />
    <product id="10" name="Widget 1" price="15.99" category_code="E" category="Electronics" manufacturer_code="SC1" manufacturer="Some Company 1" />
    <product id="10" name="Widget 1" price="15.99" category_code="T" category="Toys" manufacturer_code="SC2" manufacturer="Some Company 2" />
    <product id="10" name="Widget 1" price="15.99" category_code="E" category="Electronics" manufacturer_code="SC2" manufacturer="Some Company 2" />
    <product id="10" name="Widget 1" price="15.99" category_code="T" category="Toys" manufacturer_code="SC3" manufacturer="Some Company 3" />
    <product id="10" name="Widget 1" price="15.99" category_code="E" category="Electronics" manufacturer_code="SC3" manufacturer="Some Company 3" />
    <product id="11" name="Widget 2" price="21.99" category_code="V" category="Video Games" manufacturer_code="SC4" manufacturer="Some Company 4" />
    <product id="12" name="Widget 3" price="10.99" category_code="T" category="Toys" manufacturer_code="SC1" manufacturer="Some Company 2" />
</products>

コンマ区切りのテキスト ファイル、または次のような、各製品に対して 1 行のみを含む適切な形式の HTML テーブルに変換します。

id,name,price,category_code_1,category_1,category_code_2,category_2,manufacturer_code_1,manufacturer_1,manufacturer_code_2,manufacturer_2,manufacturer_code_3,manufacturer_3
    10, Widget 1, 15.99, T, Toys, E, Electronics, SC1, Some Company 1, SC2, Some Company 2, SC3, Some Company 3 
    11, Widget 2, 21.99, V, Video Games,,, SC4, Some Company 4,,,,
    12, Widget 3, 10.99, T, Toys,,, SC1, Some Company 2,,,,

お気づきのとおり、XML データは、product、product_category、および product_manufacturer という 3 つのテーブルを結合した結果と考えることができます。各製品は複数のカテゴリに属し、複数のメーカーを持つことができます。もちろん、私が扱っている実際のデータはより複雑で、まったく別のドメインにありますが、このサンプルは問題を適切に示しています。

私は XSLT について非常に限られた知識しか持っていませんが、SO や Internet の他のリソースの助けを借りて、必要なものを部分的に提供するスタイルシートをまとめました。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:key name="key_product_group" match="product" use="@id"/>
    <xsl:key name="key_category_group" match="product" use="concat(
             @id,
             @category_code,
             @category)"/>
    <xsl:key name="key_manufacturer_group" match="product" use="concat(
             @id,
             @manufacturer_code,
             @manufacturer)"/>
    <xsl:variable name="var_max_category_group"  >
        <xsl:for-each select="//product[generate-id(.) = generate-id(key('key_product_group',@id) )]">
            <xsl:sort select="count(key('key_product_group',@id )[generate-id() = generate-id(key('key_category_group',
                                   concat(@id,
                                  @category_code,
                                  @category)))])" order="descending"/>
            <xsl:if test="position() = 1">
                <xsl:value-of select="count(key('key_product_group',@id )[generate-id() = generate-id(key('key_category_group',
                                   concat(@id,
                                  @category_code,
                                  @category)))])"/>
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>
    <xsl:variable name="var_max_manufacturer_group">
        <xsl:for-each select="//product[generate-id(.) = generate-id(key('key_product_group',@id) )]">
            <xsl:sort select="count(key('key_product_group',@id )[generate-id() = generate-id(key('key_manufacturer_group',
                                   concat(@id,
                                  @category_code,
                                  @category)))])" order="descending"/>
            <xsl:if test="position() = 1">
                <xsl:value-of   select="count(key('key_product_group',@id )[generate-id() = generate-id(key('key_manufacturer_group',
                                   concat(@id,
                                  @manufacturer_code,
                                  @manufacturer)))])"/>
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>
    <xsl:template match="/">
        <xsl:text>id,</xsl:text>
        <xsl:text>name,</xsl:text>
        <xsl:text>price,</xsl:text>
        <xsl:call-template name="loop_pcat">
            <xsl:with-param name="count" select="$var_max_category_group"/>
        </xsl:call-template>
        <xsl:call-template name="loop_pmf">
            <xsl:with-param name="count" select="$var_max_manufacturer_group"/>
        </xsl:call-template>
        <br></br>
        <xsl:variable name="var_group"
                          select="//product[generate-id(.) = generate-id(key('key_product_group',@id)[1])]"/>
        <xsl:for-each select="$var_group">
            <xsl:sort order="ascending" select="@id"/>
            <xsl:value-of select="@id"/>,
            <xsl:value-of select="@name"/>,
            <xsl:value-of select="@price"/>,
            <xsl:for-each select="key('key_product_group',@id )[generate-id() = generate-id(key('key_category_group',
                                   concat(@id,
                                  @category_code,
                                  @category)))]">
                <xsl:value-of select="@category_code"/>,
                <xsl:value-of select="@category"/>,
            </xsl:for-each>
            <xsl:for-each select="key('key_product_group',@id )[generate-id() = generate-id(key('key_manufacturer_group',
                                   concat(@id,
                                  @manufacturer_code,
                                  @manufacturer)))]">
                <xsl:value-of select="@manufacturer_code"/>,
                <xsl:value-of select="@manufacturer"/>,
            </xsl:for-each>
            <br></br>
        </xsl:for-each>
    </xsl:template>
    <xsl:template name="loop_pcat">
        <xsl:param name="count" select="1"/>
        <xsl:param name="limit" select="$count+1"/>
        <xsl:if test="$count > 0">
            <xsl:value-of select="concat('category_code_',$limit - $count)"/>
            <xsl:text>,</xsl:text>
            <xsl:value-of select="concat('category_',$limit - $count)"/>
            <xsl:text>,</xsl:text>
            <xsl:call-template name="loop_pcat">
                <xsl:with-param name="count" select="$count - 1"/>
                <xsl:with-param name="limit" select="$limit"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
    <xsl:template name="loop_pmf">
        <xsl:param name="count" select="1"/>
        <xsl:param name="limit" select="$count+1"/>
        <xsl:if test="$count > 0">
            <xsl:value-of select="concat('manufacturer_code_',$limit - $count)"/>
            <xsl:text>,</xsl:text>
            <xsl:value-of select="concat('manufacturer_',$limit - $count)"/>
            <xsl:text>,</xsl:text>
            <xsl:call-template name="loop_pmf">
                <xsl:with-param name="count" select="$count - 1"/>
                <xsl:with-param name="limit" select="$limit"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

上記のスタイルシートは、次の結果を生成します。

id,name,price,category_code_1,category_1,category_code_2,category_2,manufacturer_code_1,manufacturer_1,manufacturer_code_2,manufacturer_2,manufacturer_code_3,manufacturer_3,
10, Widget 1, 15.99, T, Toys, E, Electronics, SC1, Some Company 1, SC2, Some Company 2, SC3, Some Company 3, 
11, Widget 2, 21.99, V, Video Games, SC4, Some Company 4, 
12, Widget 3, 10.99, T, Toys, SC1, Some Company 2, 

出力には、少なくとも 1 つの大きな問題があります。たとえば、2 行目と 3 行目で、category_code_2 と category_2、manufacture_code と、manufacturer 2 と 3 が欠落しているなど、各行にすべての列が存在しません。スタイルシートには他にも問題があると確信しています。同様に、比較的大きな xml ファイルでどのように機能するかはわかりませんが、現時点では、スタイルシートが目的の出力形式を生成するように助けていただければ幸いです。

ありがとうございました

MRSA

4

2 に答える 2

0

これが私が思いついたものです。最後に余分なコンマを処理する必要があり、属性値の単一または二重引用符などの特殊文字も処理する必要があります。コードの最適化に関するヘルプは大歓迎です。

以下のスタイルシート

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" version="1.0" encoding="UTF-8" indent="yes"/>
    <!--Generate keys, first is the product group-->
    <xsl:key name="key_product_group" match="product" use="@id"/>
    <!--Here is keys for groupings that are needed, note concatenation of attributes-->
    <xsl:key name="key_category_group" match="product" use="concat(
             @id,
             @category_code,
             @category)"/>
    <!--another key-->
    <xsl:key name="key_manufacturer_group" match="product" use="concat(
             @id,
             @manufacturer_code,
             @manufacturer)"/>
    <!--we need the maximum number of distinct groups for each product so that can format the layout properly-->
    <xsl:variable name="var_max_category_group"  >
        <xsl:for-each select="//product[generate-id(.) = generate-id(key('key_product_group',@id) )]">
            <xsl:sort select="count(key('key_product_group',@id )[generate-id() = generate-id(key('key_category_group',
                                   concat(@id,
                                  @category_code,
                                  @category)))])" order="descending"/>
            <xsl:if test="position() = 1">
                <xsl:value-of select="count(key('key_product_group',@id )[generate-id() = generate-id(key('key_category_group',
                                   concat(@id,
                                  @category_code,
                                  @category)))])"/>
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>
    <!--maximum for the second group-->
    <xsl:variable name="var_max_manufacturer_group">
        <xsl:for-each select="//product[generate-id(.) = generate-id(key('key_product_group',@id) )]">
            <xsl:sort select="count(key('key_product_group',@id )[generate-id() = generate-id(key('key_manufacturer_group',
                                   concat(@id,
                                  @category_code,
                                  @category)))])" order="descending"/>
            <xsl:if test="position() = 1">
                <xsl:value-of   select="count(key('key_product_group',@id )[generate-id() = generate-id(key('key_manufacturer_group',
                                   concat(@id,
                                  @manufacturer_code,
                                  @manufacturer)))])"/>
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>
    <!--delmiter and line break characters defined here-->
    <xsl:variable name="var_delimiter" select="','"/>
    <xsl:variable name="var_line_break" select="'&#xa;'"/>

    <!--main procedure starts here-->
    <xsl:template match="/">
        <!--create the file header-->
        <xsl:text>id</xsl:text>
        <xsl:value-of select="$var_delimiter"/>
        <xsl:text>name</xsl:text>
        <xsl:value-of select="$var_delimiter"/>
        <xsl:text>price</xsl:text>
        <xsl:value-of select="$var_delimiter"/>
        <!--recursive loop for each group to add appropriate number of columns, i.e. maximum number of repetitions-->
        <xsl:call-template name="loop_pcat_header">
            <xsl:with-param name="count" select="$var_max_category_group"/>
        </xsl:call-template>
        <!--recursive loop for each group to add appropriate number of columns, i.e. maximum number of repetitions-->
        <xsl:call-template name="loop_pmf_header">
            <xsl:with-param name="count" select="$var_max_manufacturer_group"/>
        </xsl:call-template>
        <xsl:value-of select="$var_line_break"/>
        <!--select products and order ascending, the ordering is not really needed-->
        <xsl:variable name="var_group"
                          select="//product[generate-id(.) = generate-id(key('key_product_group',@id)[1])]"/>
        <xsl:for-each select="$var_group">
            <xsl:sort order="ascending" select="@id"/>
            <!--select non-repeatable attributes-->
            <xsl:value-of select="@id"/>
            <xsl:value-of select="$var_delimiter"/>
            <xsl:value-of select="@name"/>
            <xsl:value-of select="$var_delimiter"/>
            <xsl:value-of select="@price"/>
            <xsl:value-of select="$var_delimiter"/>
            <!--select repeatable groups for each product-->
            <xsl:for-each select="key('key_product_group',@id )[generate-id() = generate-id(key('key_category_group',
                                   concat(@id,
                                  @category_code,
                                  @category)))]">
                <xsl:value-of select="@category_code"/>
                <xsl:value-of select="$var_delimiter"/>
                <xsl:value-of select="@category"/>
                <xsl:value-of select="$var_delimiter"/>
                <!--count number of groups for each product and add blanks for the difference between the max and current instance-->
                <xsl:variable name="var_max_category_group_instance" select="count(key('key_product_group',@id )[generate-id() = generate-id(key('key_category_group',
                                   concat(@id,
                                  @category_code,
                                  @category)))])"/>
                <xsl:call-template name="loop_pcat_data">
                    <xsl:with-param name="count" select="$var_max_category_group - $var_max_category_group_instance"/>
                </xsl:call-template>
            </xsl:for-each>
            <!--select repeatable groups for each product-->
            <xsl:for-each select="key('key_product_group',@id )[generate-id() = generate-id(key('key_manufacturer_group',
                                   concat(@id,
                                  @manufacturer_code,
                                  @manufacturer)))]">
                <xsl:value-of select="@manufacturer_code"/>
                <xsl:value-of select="$var_delimiter"/>
                <xsl:value-of select="@manufacturer"/>
                <xsl:value-of select="$var_delimiter"/>
                <!--count number of groups for each product and add blanks for the difference between the max and current instance-->
                <xsl:variable name="var_max_manufacturer_group_instance" select="count(key('key_product_group',@id )[generate-id() = generate-id(key('key_manufacturer_group',
                                   concat(@id,
                                  @manufacturer_code,
                                  @manufacturer)))])"/>
                <xsl:call-template name="loop_pmf_data">
                    <xsl:with-param name="count" select="$var_max_manufacturer_group - $var_max_manufacturer_group_instance"/>
                </xsl:call-template>
            </xsl:for-each>
            <xsl:value-of select="$var_line_break"/>
        </xsl:for-each>
    </xsl:template>
    <!--recursive loop templates for file header and data-->
    <xsl:template name="loop_pcat_header">
        <xsl:param name="count" select="1"/>
        <xsl:param name="limit" select="$count+1"/>
        <xsl:if test="$count > 0">
            <xsl:value-of select="concat('category_code_',$limit - $count)"/>
            <xsl:value-of select="$var_delimiter"/>
            <xsl:value-of select="concat('category_',$limit - $count)"/>
            <xsl:value-of select="$var_delimiter"/>
            <xsl:call-template name="loop_pcat_header">
                <xsl:with-param name="count" select="$count - 1"/>
                <xsl:with-param name="limit" select="$limit"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
    <xsl:template name="loop_pcat_data">
        <xsl:param name="count" select="1"/>
        <xsl:param name="limit" select="$count+1"/>
        <xsl:if test="$count > 0">
            <xsl:value-of select="$var_delimiter"/>
            <xsl:value-of select="$var_delimiter"/>
            <xsl:call-template name="loop_pcat_data">
                <xsl:with-param name="count" select="$count - 1"/>
                <xsl:with-param name="limit" select="$limit"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
    <xsl:template name="loop_pmf_header">
        <xsl:param name="count" select="1"/>
        <xsl:param name="limit" select="$count+1"/>
        <xsl:if test="$count > 0">
            <xsl:value-of select="concat('manufacturer_code_',$limit - $count)"/>
            <xsl:value-of select="$var_delimiter"/>
            <xsl:value-of select="concat('manufacturer_',$limit - $count)"/>
            <xsl:value-of select="$var_delimiter"/>
            <xsl:call-template name="loop_pmf_header">
                <xsl:with-param name="count" select="$count - 1"/>
                <xsl:with-param name="limit" select="$limit"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
    <xsl:template name="loop_pmf_data">
        <xsl:param name="count" select="1"/>
        <xsl:param name="limit" select="$count+1"/>
        <xsl:if test="$count > 0">
            <xsl:value-of select="$var_delimiter"/>
            <xsl:value-of select="$var_delimiter"/>
            <xsl:call-template name="loop_pmf_data">
                <xsl:with-param name="count" select="$count - 1"/>
                <xsl:with-param name="limit" select="$limit"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

この XML に適用される場合

<?xml version="1.0" encoding="UTF-8"?>
<products>
    <product id="10" name="Widget 1" price="15.99" category_code="T" category="Toys" manufacturer_code="SC1" manufacturer="Some Company 1" />
    <product id="10" name="Widget 1" price="15.99" category_code="E" category="Electronics" manufacturer_code="SC1" manufacturer="Some Company 1" />
    <product id="10" name="Widget 1" price="15.99" category_code="T" category="Toys" manufacturer_code="SC2" manufacturer="Some Company 2" />
    <product id="10" name="Widget 1" price="15.99" category_code="E" category="Electronics" manufacturer_code="SC2" manufacturer="Some Company 2" />
    <product id="10" name="Widget 1" price="15.99" category_code="T" category="Toys" manufacturer_code="SC3" manufacturer="Some Company 3" />
    <product id="10" name="Widget 1" price="15.99" category_code="E" category="Electronics" manufacturer_code="SC3" manufacturer="Some Company 3" />
    <product id="11" name="Widget 2" price="21.99" category_code="V" category="Video Games" manufacturer_code="SC4" manufacturer="Some Company 4" />
    <product id="12" name="Widget 3" price="10.99" category_code="T" category="Toys" manufacturer_code="SC1" manufacturer="Some Company 2" />
</products>

次の出力が生成されます。

id,name,price,category_code_1,category_1,category_code_2,category_2,manufacturer_code_1,manufacturer_1,manufacturer_code_2,manufacturer_2,manufacturer_code_3,manufacturer_3,
10,Widget 1,15.99,T,Toys,E,Electronics,SC1,Some Company 1,SC2,Some Company 2,SC3,Some Company 3,
11,Widget 2,21.99,V,Video Games,,,SC4,Some Company 4,,,,,
12,Widget 3,10.99,T,Toys,,,SC1,Some Company 2,,,,,
于 2011-05-09T20:52:06.577 に答える