使用XSLT进行XML排序 - 中间名称空间

时间:2013-06-14 16:54:56

标签: xml xslt

我从xml.com中抽取样本来证明我遇到的问题。

我有这个XML:

<?xml version="1.0"?>

<!--slightly modified source from xml.com -->

<winelist xmlns="urn:somesite:api:base">

    <wine grape="Chardonnay">
        <winery>Lindeman's</winery>
        <product>Bin 65</product>
        <year>1998</year>
        <prices>
        <list>6.99</list>
            <discounted>5.99</discounted>
            <case>71.50</case>
        </prices>
    </wine>

    <wine grape="Chardonnay">
        <winery>Benziger</winery>
        <product>Carneros</product>
        <year>1997</year>
        <prices>
            <list>10.99</list>
            <discounted>9.50</discounted>
            <case>114.00</case>
        </prices>
    </wine>

    <wine grape="Cabernet">
        <winery>Duckpond</winery>
        <product>Merit Selection</product>
        <year>1996</year>
        <prices>
            <list>13.99</list>
            <discounted>11.99</discounted>
            <case>143.50</case>
        </prices>
    </wine>

    <wine grape="Chardonnay">
        <winery>Kendall Jackson</winery>
        <product>Vintner's Reserve</product>
        <year>1998</year>
        <prices>
            <list>12.50</list>
            <discounted>9.99</discounted>
            <case>115.00</case>
        </prices>
    </wine>

</winelist>

这个XSL:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:template match="winelist">
        <xsl:copy>
            <xsl:apply-templates>
                <xsl:sort data-type="number" select="prices/discounted"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

我正在尝试按折扣价对wine元素进行排序,但转换后的XML仍然未排序,除非我先从winelist中删除命名空间(即只使用<winelist>)。

如何修改XSLT,以便不必手动删除命名空间?

此外,转换后的XML中的wine实体缺少原始的grape属性。如何保存这些?评论也是如此(不过那么重要)。

我可以先使用另一个转换来删除所有命名空间,但我不太喜欢这个两步解决方案,而且我认为它可能是其他XML源问题的根源。

有人可以帮助我吗?

2 个答案:

答案 0 :(得分:0)

您唯一需要做的就是将命名空间添加到XSLT并将match="*"模板修改为identity transform

示例:

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

    <xsl:template match="x:winelist">
        <xsl:copy>
            <xsl:apply-templates>
                <xsl:sort data-type="number" select="x:prices/x:discounted"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

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

</xsl:stylesheet>

local-name()如果命名空间未知(使用Xalan和Saxon 6.5.5测试)的示例:

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

    <xsl:template match="*[local-name()='winelist']">
        <xsl:copy>
            <xsl:apply-templates>
                <xsl:sort data-type="number" select="*[local-name()='prices']/*[local-name()='discounted']"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

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

</xsl:stylesheet>

答案 1 :(得分:-1)

首先,要制作输入XML的忠实副本,请使用以下内容(称为“身份转换”):

 <xsl:template match="@*|*|processing-instruction()|comment()">
    <xsl:copy>
      <xsl:apply-templates select="@*|*|text()|processing-instruction()|comment()"/>
    </xsl:copy>
  </xsl:template>

其次,为了让你的匹配器忽略命名空间(如果那是你真正想要做的),你可以通过local-name匹配:

<xsl:template match="*[local-name()='winelist']">

或者,您可以让您的XSLT知道命名空间并在该命名空间中显式匹配,这是更常用的方法。你不想这样做的唯一原因是你事先不知道命名空间。

<xsl:stylesheet xmlns:w="urn:somesite:api:base">
...
   <xsl:template match="w:wine">
     ...