0

我有一个像
XML Input 这样的输入 xml:

    <parent>
        <child>
            <child2 name="Action">Change</child2>
            <child1 name="Change">

                <child2 name="Dest_Author">Author1</child2>

                <child2 name="Book1">BookID1</child2>
                <child2 name="Type1">General</child2>
                <child2 name="Pages1">100</child2>

                <child2 name="Book2">BookID2</child2>
                <child2 name="Type2">Cooking</child2>
                <child2 name="Pages2">200</child2>

                <child2 name="Orig_Author">Author2</child2>

                <child2 name="Book1">BookID3</child2>
                <child2 name="Type1">General</child2>
                <child2 name="Pages1">150</child2>

                <child2 name="Book2">BookID4</child2>
                <child2 name="Type2">Cooking</child2>
                <child2 name="Pages2">120</child2>

            </child1>
        </child>
    </parent>

我想使用 xslt 转换将其转换为以下 xml
预期的 XML 输出:

    <list>
        <author id="Author1">
            <book>
                <id>BookID1</id>
                <type>General</type>
                <pages>100</pages>
            </book>
            <book>
                <id>BookID1</id>
                <type>Cooking</type>
                <pages>200</pages>
            </book>
        </author>    
        <author id="Author2">
            <book>
                <id>BookID1</id>
                <type>General</type>
                <pages>150</pages>
            </book>
            <book>
                <id>BookID1</id>
                <type>Cooking</type>
                <pages>120</pages>
            </book>
        </author>
    </list>

我尝试使用 xslt 对其进行转换,但无法这样做。我的 xslt 如下所示

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  version="1.0">
    <xsl:output method="xml" omit-xml-declaration="yes" />
    <xsl:strip-space elements="*" />
    <xsl:template match="/">
        <xsl:apply-templates />
    </xsl:template>
    <xsl:template match="parent">
        <list>
            <xsl:apply-templates />
        </list>
    </xsl:template>
    <xsl:template match="child">
        <xsl:apply-templates />
    </xsl:template>
    <xsl:template match="child1">
        <xsl:choose>
            <xsl:when test="./@name='Change'">
                <author>
                    <xsl:attribute name="id"><xsl:value-of
                        select="./child2[@name='Dest_Author']" /></xsl:attribute>
                    <xsl:apply-templates />
                </author>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
    <xsl:template match="child2">
        <xsl:choose>
            <xsl:when test="contains(@name,'Orig_Author')">
                <!-- I thought of changing the below code(line no 36-38) as
                </author>
                <author>
                    <xsl:attribute name="id"><xsl:value-of select="text()"/></xsl:attribute>
                but if do this it throws error "The element type "xsl:when" must be terminated by the matching end-tag "</xsl:when>"."
            -->
                <author>
                    <xsl:attribute name="id"><xsl:value-of select="text()"/></xsl:attribute>
                </author>
            </xsl:when>

            <xsl:when test="contains(@name,'Book')">
                <book>
                    <id>
                        <xsl:value-of select="text()" />
                    </id>
                    <xsl:variable name="next"
                        select="./following-sibling::node()[@name!=''][1]"></xsl:variable>
                    <xsl:choose>
                        <xsl:when test="contains($next/@name,'Type')">
                            <type>
                                <xsl:value-of select="$next" />
                            </type>
                            <pages>
                                <xsl:value-of select="$next/following-sibling::node()[@name!=''][1]" />
                            </pages>
                        </xsl:when>
                        <xsl:when test="contains($next/@name,'Pages')">
                            <pages>
                                <xsl:value-of select="$next" />
                            </pages>
                        </xsl:when>
                    </xsl:choose>
                </book>
            </xsl:when>
            <xsl:otherwise>    
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

XSLT 输出:

<list>
    <author id="Author1">
        <book>
            <id>BookID1</id>
            <type>General</type>
            <pages>100</pages>
        </book>
        <book>
            <id>BookID2</id>
            <type>Cooking</type>
            <pages>200</pages>
        </book>
        <author id="Author2"/>
        <book>
            <id>BookID3</id>
            <type>General</type>
            <pages>150</pages>
        </book>
        <book>
            <id>BookID4</id>
            <type>Cooking</type>
            <pages>120</pages>
        </book>
    </author>
</list>

有人可以帮助我并告诉 xslt 代码需要进行哪些更改才能获得正确的输出

4

1 回答 1

0

我会将其视为分组问题。您希望将child2名称不包含的每个附加到其名称包含的最近_Author的前一个同级,_Author为每个组生成一个author元素。然后在组中,您希望将每个非Book元素附加到其最近的前面Book并为每个组生成一个book元素。

XSLT 具有可用于对元素进行分组的键的概念。我会定义两个键,首先

<xsl:key name="booksByAuthor" match="child2[starts-with(@name, 'Book')]"
   use="generate-id(preceding-sibling::child2[contains(@name, '_Author')][1])" />

它采用每个Book(ie ) 并根据最近的前作者child2[@name = 'Book1' or @name='Book2' or ....]的身份 () 为其分配一个分组键。generate-id第二把钥匙

<xsl:key name="bookDetails"
   match="child2[not(contains(@name, '_Author') or starts-with(@name, 'Book'))]"
   use="generate-id(preceding-sibling::child2[starts-with(@name, 'Book')][1])" />

将所有“详细信息”元素(不是Something_Author或的那些BookN)按最近的前面的 Book 分组。这些键为您提供层次结构,并让您将样式表的其余部分编码为正常的模板匹配系统。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  version="1.0">
    <xsl:output method="xml" omit-xml-declaration="yes" />
    <xsl:strip-space elements="*" />

    <xsl:key name="booksByAuthor" match="child2[starts-with(@name, 'Book')]"
       use="generate-id(preceding-sibling::child2[contains(@name, '_Author')][1])" />

    <xsl:key name="bookDetails"
       match="child2[not(contains(@name, '_Author') or starts-with(@name, 'Book'))]"
       use="generate-id(preceding-sibling::child2[starts-with(@name, 'Book')][1])" />

    <xsl:template match="/parent">
        <list>
            <!-- process all the authors -->
            <xsl:apply-templates select=".//child2[contains(@name, '_Author')]" />
        </list>
    </xsl:template>

    <xsl:template match="child2[contains(@name, '_Author')]">
        <!-- for each author, create an author element -->
        <author id="{.}">
            <!-- and process this author's books -->
            <xsl:apply-templates select="key('booksByAuthor', generate-id())" />
        </author>
    </xsl:template>

    <xsl:template match="child2[starts-with(@name, 'Book')]">
        <!-- for each book, create a book element -->
        <book>
            <id><xsl:value-of select="." /></id>
            <!-- and process this book's details -->
            <xsl:apply-templates select="key('bookDetails', generate-id())" />
        </book>
    </xsl:template>

    <xsl:template match="child2[starts-with(@name, 'Type')]">
        <type><xsl:value-of select="." /></type>
    </xsl:template>

    <xsl:template match="child2[starts-with(@name, 'Pages')]">
        <pages><xsl:value-of select="." /></pages>
    </xsl:template>
</xsl:stylesheet>

如果您有其他子类型,例如Publisher1, Publisher2, ... 那么您只需添加与Typeand相同的其他模板Pages

于 2013-09-26T10:14:56.747 回答