使用xslt,如何添加一个在第一次出现之前开始的xml节点,并在最后一次出现项目后结束?

时间:2013-08-26 17:34:04

标签: xml xslt

我有一个看起来像这样的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>

我想将“items”包装到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>

“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>

3 个答案:

答案 0 :(得分:3)

您的XSLT必须是格式良好的XML,因此您无法在一个xsl:if中启动元素并在另一个元素中关闭它。

就像C. M. Sperberg-McQueen建议的那样,从identity transform开始并从那里覆盖。

示例(如果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。)

答案 1 :(得分: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>

答案 2 :(得分: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></GroceryList>视为单独的说明。模板主体包含三个指令序列:apply-templates,GroceryList和apply-templates,GroceryList指令包含内容(“序列构造函数”),其中包含生成GroceryList节点内容的指令。