2

我有一个看起来像这样的 xml:

<ShoppingList>
    <MoneyIHaveToSpend>20.00</MoneyIHaveToSpend>
    <Item>
        <Name>Apples</Name>
        <Price>1.00</Price>
    </Item>
    <Item>
        <Name>Oranges</Name>
        <Price>1.00</Price>
    </Item>
    <AdditionalInfo>...</AdditionalInfo>
</ShoppingList>

我想将“项目”包装到 GroceryList 中,如下所示:

<ShoppingList>
    <MoneyIHaveToSpend>20.00</MoneyIHaveToSpend>
    <GroceryList>
        <Item>
            <Name>Apples</Name>
            <Price>1.00</Price>
        </Item>
        <Item>
            <Name>Oranges</Name>
            <Price>1.00</Price>
        </Item>
    </GroceryList>
    <AdditionalInfo>...</AdditionalInfo>
</ShoppingList>

“购物清单”内只有一个子清单。我将如何使用 xslt 执行此操作?

我试图做类似以下的事情,但分裂<GroceryList></GroceryList>给我编译错误。我意识到其余的无论如何都是错误的,但是由于编译错误,我什至无法对其进行测试或弄乱它:

<xsl:template match="@* | node()">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="ShoppingList">
    <xsl:apply-templates select="/ShoppingList/Item"/>
    <xsl:if test="position() = 1">
        <GroceryList> <!-- Error: "XML element is not closed" -->
        <xsl:apply-templates/>
    </xsl:if>
    <xsl:if test="???">
        </GroceryList> <!-- Error: "No open tag found" -->
    </xsl:if>
</xsl:template>
4

3 回答 3

3

您的 XSLT 必须是格式良好的 XML,因此您不能在一个元素中开始xsl:if并在另一个元素中关闭它。

就像 CM Sperberg-McQueen 建议的那样,从身份转换开始并从那里覆盖。

示例(如果ShoppingList孩子的顺序无关紧要):

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

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="ShoppingList">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()[not(self::Item)]"/>
            <GroceryList>
                <xsl:apply-templates select="Item"/>
            </GroceryList>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

如果订单确实很重要,您可以执行以下操作:

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

    <xsl:template match="@*|node()" name="ident">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Item[1]">
        <GroceryList>
            <xsl:apply-templates select=".|following-sibling::Item" mode="keep"/>
        </GroceryList>
    </xsl:template>

    <xsl:template match="Item" mode="keep">
        <!--You could overwrite identity transform here.-->
        <xsl:call-template name="ident"/>
    </xsl:template>

    <xsl:template match="Item"/>

</xsl:stylesheet>

以上内容非常通用,但如果您始终知道孩子ShoppingList将拥有什么或者您不需要修改任何元素,则可以简化。(您可以使用xsl:copy-of而不是xsl:apply-templates。)

于 2013-08-26T18:28:17.250 回答
1

节点不是“开始”和“结束”,它们是具有父母和孩子的树中的点。您正在尝试编写 XSLT,就好像它将开始和结束标记输出为可分离的操作,而不是构建节点树。树上的节点是不可分割的。

如果您的分组问题与示例所示的一样简单,那么最简单的解决方案将使用如下模板:

<xsl:template match="ShoppingList">
  <xsl:apply-templates select="MoneyIHaveToSpend"/>
  <GroceryList>
    <xsl:apply-templates select="Item"/>
  </GroceryList>
  <xsl:apply-templates select="AdditionalInfo"/>
</xsl:template>

当您阅读本文时,不要将<GroceryList>and</GroceryList>视为单独的说明。模板主体包含三个指令的序列:apply-templates、GroceryList 和 apply-templates,并且 GroceryList 指令的内容(“序列构造函数”)包含生成 GroceryList 节点内容的指令。

于 2013-08-26T22:59:44.347 回答
1

另一种解决方案:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output encoding="utf-8" method="xml"/>

    <xsl:template match="ShoppingList">

        <ShoppingList>
            <xsl:variable name="firstItemName"><xsl:value-of select="./Item/Name/text()"/></xsl:variable>
            <xsl:apply-templates>
                <xsl:with-param name="firstItem"><xsl:value-of select="$firstItemName"/></xsl:with-param>
            </xsl:apply-templates>
        </ShoppingList>
    </xsl:template>

    <xsl:template match="Item">
        <xsl:param name="firstItem"/>

        <xsl:choose>
            <xsl:when test="./Name/text()=$firstItem">
                <xsl:element name="GroceryList">
                    <xsl:apply-templates select="../Item"/>
                </xsl:element>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy-of select="."/>
            </xsl:otherwise>
        </xsl:choose>

        </xsl:template>

    <xsl:template match="MoneyIHaveToSpend|AdditionalInfo|text()">
        <xsl:copy-of select="."/>
    </xsl:template>

</xsl:stylesheet>
于 2013-08-26T19:04:19.110 回答