【问题标题】:XML sorting with XSLT - namespace in the middle使用 XSLT 进行 XML 排序 - 名称空间在中间
【发布时间】:2013-06-14 16:54:55
【问题描述】:

我从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 仍然未排序,除非我首先从酒单中删除命名空间(即仅使用 &lt;winelist&gt;)。

如何修改 XSLT 以便不必手动移除名称空间?

此外,转换后的 XML 中的葡萄酒实体缺少其原始的 grape 属性。这些怎么保存? cmets 也是如此(不过没那么重要)。

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

有人可以帮我吗?

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    您唯一需要做的就是将命名空间添加到 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>
    

    【讨论】:

    • 是的,这行得通。但是我从来没有考虑过在编写样式表之前我可能不知道命名空间。无论如何,我应该假设您的身份转换与其他发布者的回复中显示的相同,还是它们之间存在差异?
    • @Alvein - 身份转换是相同的。 node()包括元素(*),文本(text()),处理指令(processing-instruction(),和cmets(comment())。如果不知道命名空间,可以测试local-name()。我将添加一个示例。
    【解决方案2】:

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

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

    其次,为了让您的匹配器忽略命名空间(如果这是您真正想要做的),您可以通过本地名称进行匹配:

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

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

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

    【讨论】:

    • 你确定这个
    • @Alvein - 使用local-name() 应该可以正常工作。你用的是什么处理器?
    • 好吧,如您所见,您还必须编辑 sort 元素的 select 属性。这对我来说并不明显。谢谢大家。
    猜你喜欢
    • 1970-01-01
    • 2019-11-26
    • 2011-07-13
    • 1970-01-01
    • 2010-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多