使用XSLT添加属性,然后使用该新属性进行排序
我熟悉XSLT的基础知识,但遇到了一个我似乎无法理解的奇怪情况。我很抱歉这么长时间,但我真的很感激你能提供的任何帮助 我正在对一个我无法控制的软件产品生成的XML进行转换。该产品导出的数据如下所示:使用XSLT添加属性,然后使用该新属性进行排序,xslt,sorting,Xslt,Sorting,我熟悉XSLT的基础知识,但遇到了一个我似乎无法理解的奇怪情况。我很抱歉这么长时间,但我真的很感激你能提供的任何帮助 我正在对一个我无法控制的软件产品生成的XML进行转换。该产品导出的数据如下所示: <header> <data> </data> </header> <transaction> <B1_PG1 ts='1139977698718.75'><data></data>&
<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)
然后,我根据元素名称查找该序列,将其与时间戳连接,并使用以下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。。。使转换尽可能可移植。出于性能原因,我还希望避免执行两个转换。谢谢 您遇到的问题就在这段代码中
<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>
请注意,最好对每一个应用模板,这就是我在这里所做的。您遇到的问题就在这段代码中
<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>
请注意,最好对每个元素使用应用模板,这就是我在这里所做的。您创建的属性存在于您放置在结果树上的新构造元素上,但不存在于您正在排序的源树中的元素上 另一个问题是,您没有对事务元素的子元素进行排序,而是对其子元素进行排序 我想你想要的是:
<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中,一次生成输出属性)的唯一方法是进行两次传递,一次添加属性,另一次对其进行排序。在XSLT1.0和XSLT2.0之间,实现方法各不相同,您没有说明使用的是哪一种。两次通过的方法是非常可行的,但在您的情况下,我怀疑重复计算的单次通过更有效(尽管您必须同时测量这两种方法才能找到答案)。您创建的属性出现在您放置在结果树上的新构造元素上,但是它不存在于您正在排序的源树中的元素上 另一个问题是,您没有对事务元素的子元素进行排序,而是对其子元素进行排序 我想你想要的是:
<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>