1

我熟悉 XSLT 的基础知识,但我遇到了一个我似乎无法弄清楚的奇怪情况。对于这么长时间,我深表歉意,但我非常感谢您能提供的任何帮助。

我正在对由我无法控制的软件产品生成的 XML 进行转换。该产品导出的数据如下所示:

<header>
   <data>
   </data>
</header>
<transaction>
    <B1_PG1 ts='1139977698718.75'><data></data></B1_PG1>
    <B1_PG2 ts='1139977698718.76'><data></data></B1_PG2>
    <B2_PG1 ts='1139977698718.77'><data></data></B2_PG1>
    <B2_PG2 ts='1139977698718.78'><data></data></B2_PG2>
    <B2_PG1 ts='1139977698718.79'><data></data></B2_PG1>
    <B2_PG2 ts='1139977698718.80'><data></data></B2_PG2>
    <B3_PG1 ts='1139977698718.81'><data></data></B3_PG1>
</transaction>

软件产品按照接收数据的顺序导出数据页面。我需要获取这些页面并按自定义顺序对它们进行排序,以便处理到另一个系统中。因此,我创建了一个看起来像这样的查找文档来定义我的自定义排序顺序:

(PageSequences.xml)

<pages>
    <page id="B2_PG1" sequence="1000" />
    <page id="B2_PG2" sequence="1010" />
    <page id="B3_PG1" sequence="2000" />
    <page id="B1_PG1" sequence="3000" />
    <page id="B1_PG2" sequence="3010" />
</pages>

然后,我根据元素名称查找该序列,将其与时间戳连接,并使用以下 XSLT 将属性注入元素:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output omit-xml-declaration="yes" indent="yes"/> 
    <xsl:strip-space elements="*"/> 
    <xsl:variable name='page-seqs' select='document("PageSequences.xml")/pages/page'/>

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

    <xsl:template match="transaction">
        <transaction>

            <xsl:for-each select="child::node()">
                <xsl:variable name='localname' select='local-name()'/>
                <xsl:copy>
                    <xsl:attribute name="sequence">
                        <xsl:value-of select='$page-seqs[@id=$localname]/@sequence'/>-<xsl:value-of select='@ts'/>
                    </xsl:attribute>
                    <xsl:apply-templates select="@*|node()">
                        <xsl:sort select="@sequence" />
                    </xsl:apply-templates>
                </xsl:copy> 
            </xsl:for-each>
        </transaction>
        </xsl:template> 

</xsl:stylesheet> 

我遇到的问题是标签似乎不起作用。我期望以下内容:

<transaction>
    <B2_PG1 ts='1139977698718.77' sequence='1000-1139977698718.77'><data></data></B2_PG1>
    <B2_PG1 ts='1139977698718.79' sequence='1000-1139977698718.79'><data></data></B2_PG1>
    <B2_PG2 ts='1139977698718.78' sequence='1010-1139977698718.78'><data></data></B2_PG2>
    <B2_PG2 ts='1139977698718.80' sequence='1010-1139977698718.80'><data></data></B2_PG2>
    <B3_PG1 ts='1139977698718.81' sequence='2000-1139977698718.81'><data></data></B3_PG1>
    <B1_PG1 ts='1139977698718.75' sequence='3000-1139977698718.75'><data></data></B1_PG1>
    <B1_PG2 ts='1139977698718.76' sequence='3010-1139977698718.76'><data></data></B1_PG2>
</transaction>

但我得到:

<transaction>
    <B1_PG1 ts='1139977698718.75' sequence='3000-1139977698718.75'><data></data></B1_PG1>
    <B1_PG2 ts='1139977698718.76' sequence='3010-1139977698718.76'><data></data></B1_PG2>
    <B2_PG1 ts='1139977698718.77' sequence='1000-1139977698718.77'><data></data></B2_PG1>
    <B2_PG2 ts='1139977698718.78' sequence='1010-1139977698718.78'><data></data></B2_PG2>
    <B2_PG1 ts='1139977698718.79' sequence='1000-1139977698718.79'><data></data></B2_PG1>
    <B2_PG2 ts='1139977698718.80' sequence='1010-1139977698718.80'><data></data></B2_PG2>
    <B3_PG1 ts='1139977698718.81' sequence='2000-1139977698718.81'><data></data></B3_PG1>
</transaction>

此外,如果您认为我以错误的方式处理此问题,请告诉我。我试图避免使用 java/c#/perl/etc... 以尽可能保持转换的可移植性。出于性能原因,我还想避免执行两次转换。谢谢!

4

2 回答 2

1

您创建的属性存在于您放置在结果树上的新构造元素上,但它不存在于您正在排序的源树中的元素上。

另一个问题是您不是对事务元素的子元素进行排序,而是对它的孙元素进行排序。

我怀疑你想要的是这样的:

           <xsl:for-each select="child::node()">
              <xsl:sort select="concat($page-seqs[@id=local-name(current())]/@sequence, '-', @ts)"/>
                <xsl:variable name='localname' select='local-name()'/>
                <xsl:copy>
                    <xsl:attribute name="sequence">
                        <xsl:value-of select='$page-seqs[@id=$localname]/@sequence'/>-<xsl:value-of select='@ts'/>
                    </xsl:attribute>
                    <xsl:apply-templates select="@*|node()"/>
                </xsl:copy> 
            </xsl:for-each>

避免重复计算的唯一方法(一次在 xsl:sort 中,一次生成输出属性)是执行两次传递,一次添加属性,下一次对其进行排序。这样做的方式在 XSLT 1.0 和 2.0 之间有所不同,而且您没有说您使用的是哪个。两遍方法非常可行,但在您的情况下,我怀疑重复计算的单遍更有效(尽管您必须同时测量两者才能找出答案)。

于 2012-07-15T08:18:48.397 回答
0

您遇到的问题在此代码块中

<xsl:copy> 
   <xsl:attribute name="sequence"> 
      <xsl:value-of select='$page-seqs[@id=$localname]/@sequence'/>-<xsl:value-of select='@ts'/> 
   </xsl:attribute> 
   <xsl:apply-templates select="@*|node()"> 
      <xsl:sort select="@sequence" /> 
   </xsl:apply-templates> 
</xsl:copy>  

特别是应用模板。首先,在这个阶段你已经复制并输出了“B-PG”元素,所有的应用模板正在处理它的子节点。其次,排序仅适用于输入文档,而不适用于您添加到输出文档的任何额外属性。

然后,您可以做的是将复制事务子节点的结果放入变量中,然后使用排序迭代该变量。这将是同一个 XSLT 文档中的“两次转换”。但是,在这种情况下,没有必要。您可以简单地匹配所有事务子节点,并指定在排序中查找序列号

<xsl:sort select="$page-seqs[@id=local-name(current())]/@sequence" />

这是完整的 XSLT

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

   <xsl:variable name="page-seqs" select="document('C:\lookup.xml')/pages/page"/>

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

   <xsl:template match="transaction">
      <transaction>
         <xsl:apply-templates select="child::node()">
            <xsl:sort select="$page-seqs[@id=local-name(current())]/@sequence"/>
         </xsl:apply-templates>
      </transaction>
   </xsl:template>
</xsl:stylesheet>

当应用于您的示例文档时(减去标题元素,因为当前 XML 示例格式不正确),输出以下内容

<transaction>
   <B2_PG1 ts="1139977698718.77">
      <data/>
   </B2_PG1>
   <B2_PG1 ts="1139977698718.79">
      <data/>
   </B2_PG1>
   <B2_PG2 ts="1139977698718.78">
      <data/>
   </B2_PG2>
   <B2_PG2 ts="1139977698718.80">
      <data/>
   </B2_PG2>
   <B3_PG1 ts="1139977698718.81">
      <data/>
   </B3_PG1>
   <B1_PG1 ts="1139977698718.75">
      <data/>
   </B1_PG1>
   <B1_PG2 ts="1139977698718.76">
      <data/>
   </B1_PG2>
</transaction>

请注意,最好使用应用模板而不是 for-each,这就是我在这里所做的。

于 2012-07-15T08:14:44.557 回答