我有一个巨大的XML格式的配置文件。系统不关心标签的顺序,但我们人类做的! (主要是出于版本比较的目的。)I already received下面的XSLT运行良好,但我发现它还不够。
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates>
<xsl:sort select="(@name, name())[1]"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
我希望通过其name
属性的值递归地对所有标签进行排序(这有效!)但是因为该属性并不总是存在,所以它还必须按其他属性排序,任何给定元素中可能存在或不存在任何元素。
我对XSLT基本上没有理解,所以我正在试验。我已将上述内容攻入此内容,但它并没有按预期工作。其结果似乎与上述相同。
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates>
<xsl:sort select="@name"/>
<xsl:sort select="@row" data-type="number"/>
<xsl:sort select="@col" data-type="number"/>
<xsl:sort select="@sequence" data-type="number"/>
<xsl:sort select="@tabindex" data-type="number"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
我的数据看起来与此类似,问题是cell
元素根本没有排序(在grid
组内),因为它们没有name
属性。这就是为什么我想扩展排序逻辑以在存在时使用name
属性,否则应该使用tabindex
之类的其他属性来完成排序。在任何给定的组中,可以假定存在相同的属性。
<sections>
<section name="SomeList">
<caption>
<![CDATA[Candidates]]>
</caption>
...
<parameters>
<parameter name="pageSize">
<![CDATA[50]]>
</parameter>
</parameters>
...
<grid>
<cell row="0" col="7" tabindex="9" colspan="10">
<field name="Entered" />
</cell>
</grid>
</section>
</sections>
更新
在Vincent的帮助下,我创建了一个适合我们目的的分类。 Here it is.
答案 0 :(得分:4)
这是一种回应,假设您的数据中没有任何混合内容。它只考虑了两个第一步(@name和@col),您可以适应进一步的步骤。也许它可以用递归命名模板重写,该模板将排序参数列表作为输入。如果我的XSLT不适合你,你能提供XML样本。
XSLT 2.0示例:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:for-each-group select="*" group-by="if (exists(@name)) then @name else ''">
<xsl:sort select="current-grouping-key()" data-type="text"/>
<xsl:for-each-group select="current-group()" group-by="if (exists(@row)) then @row else -1">
<xsl:sort select="current-grouping-key()" data-type="number"/>
<xsl:apply-templates select="current-group()"/>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
请注意,代码会对具有相同值的组进行迭代,因此,如果元素上不存在属性,则元素将组合在一起。
我将以下XML作为输入:
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item row="5" col="9"></item>
<item name="d" row="20" col="12" tabindex="" sequence=""></item>
<item row="1" col="5" ></item>
<item name="d" row="5" col="6" ></item>
<item name="a" row="7" col="8" ></item>
<item name="s" row="1" col="5" ></item>
<item name="c" row="5" col="9"></item>
<item row="2" col="5" ></item>
<item row="20" col="9"></item>
<item row="0" col="9"></item>
<item name="s" row="2" col="10" tabindex="" sequence=""></item>
<item name="z" row="8" col="15" tabindex="" sequence=""></item>
</items>
我有以下结果:
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item row="0" col="9"/>
<item row="1" col="5"/>
<item row="2" col="5"/>
<item row="5" col="9"/>
<item row="20" col="9"/>
<item name="a" row="7" col="8"/>
<item name="c" row="5" col="9"/>
<item name="d" row="5" col="6"/>
<item name="d" row="20" col="12" tabindex="" sequence=""/>
<item name="s" row="1" col="5"/>
<item name="s" row="2" col="10" tabindex="" sequence=""/>
<item name="z" row="8" col="15" tabindex="" sequence=""/>
</items>
答案 1 :(得分:1)
将此XSLT视为具有给定强制属性的特定元素:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates>
<xsl:sort select="(@name, name())[1]"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="grid">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:for-each-group select="*" group-by="if (exists(@row)) then @row else -1">
<xsl:sort select="current-grouping-key()" data-type="number"/>
<xsl:for-each-group select="current-group()" group-by="if (exists(@col)) then @col else -1">
<xsl:sort select="current-grouping-key()" data-type="number"/>
<xsl:apply-templates select="current-group()"/>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
我的示例必须涵盖使用匹配*的第一个模板排序的部分和参数。并且还按行和列进行网格排序。 您可以通过复制模板来扩展具有不同排序属性的任何其他元素。
如果您有相同属性的多个元素,请使用match="elt1|elt2|elt3"
。
答案 2 :(得分:1)
这是一个通用,简单且不长(60格式良好的行)解决方案。
对 所有 所需属性执行排序,这不需要任何手动复制模板:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my" exclude-result-prefixes="my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pSortTypes" as="element()*">
<attr name="name" type="alpha" maxLength="15"/>
<attr name="row" type="numeric" maxLength="6"/>
<attr name="col" type="numeric" maxLength="4"/>
<attr name="tabindex" type="numeric" maxLength="2"/>
<attr name="sequence" type="numeric" maxLength="3"/>
</xsl:param>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="*">
<xsl:sort select="my:OrderedAttributeTuple(.)"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:function name="my:OrderedAttributeTuple" as="xs:string">
<xsl:param name="pElem" as="element()"/>
<xsl:variable name="vResult" as="xs:string*">
<xsl:apply-templates select="$pSortTypes">
<xsl:with-param name="pElem" select="$pElem"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:sequence select="string-join($vResult, '')"/>
</xsl:function>
<xsl:template match="attr">
<xsl:param name="pElem" as="element()"/>
<xsl:variable name="vVal" select=
"string($pElem/@*[name() eq current()/@name])"/>
<xsl:variable name="vPad" as="xs:string*" select=
"for $cnt in xs:integer(@maxLength) - string-length($vVal),
$i in 1 to $cnt
return '.'
"/>
<xsl:variable name="vPadding" select="string-join($vPad, '')"/>
<xsl:variable name="vTuple">
<xsl:sequence select=
"if(@type eq 'alpha')
then concat($vVal, $vPadding)
else concat($vPadding, $vVal)
"/>
</xsl:variable>
<xsl:sequence select="string($vTuple)"/>
</xsl:template>
</xsl:stylesheet>
对此XML文档应用此转换时:
<items>
<item row="5" col="9"/>
<item name="d" row="20" col="12" tabindex="" sequence=""/>
<item row="1" col="5" />
<item name="d" row="5" col="6" />
<item name="a" row="7" col="8" />
<item name="s" row="1" col="5" tabindex="3" sequence="4"/>
<item name="s" row="3" col="3" tabindex="3" sequence="4"/>
<item name="c" row="5" col="9"/>
<item row="2" col="5" />
<item row="20" col="9"/>
<item row="0" col="9"/>
<item name="s" row="3" col="3" tabindex="1" sequence="2"/>
<item name="s" row="2" col="10" tabindex="1" sequence="2"/>
<item name="z" row="8" col="15" tabindex="" sequence=""/>
</items>
生成了正确排序的结果:
<items>
<item row="0" col="9"/>
<item row="1" col="5"/>
<item row="2" col="5"/>
<item row="5" col="9"/>
<item row="20" col="9"/>
<item name="a" row="7" col="8"/>
<item name="c" row="5" col="9"/>
<item name="d" row="5" col="6"/>
<item name="d" row="20" col="12" tabindex="" sequence=""/>
<item name="s" row="1" col="5" tabindex="3" sequence="4"/>
<item name="s" row="2" col="10" tabindex="1" sequence="2"/>
<item name="s" row="3" col="3" tabindex="1" sequence="2"/>
<item name="s" row="3" col="3" tabindex="3" sequence="4"/>
<item name="z" row="8" col="15" tabindex="" sequence=""/>
</items>
请注意:
在外部参数($pSortTypes
)中指定的 所有 属性上执行排序。将此与当前接受的答案进行比较,该答案仅对@name
和@row
进行排序,并且需要对订单进行硬编码并对数据类型进行排序。
可以指定属性的确切排序顺序。这是他们的订单,如$pSortTypes
。
type
中的$pSortTypes
属性(目前只有"alpha"
和"numeric"
中指定了每个属性的排序数据类型 })
指定属性值的字符串表示的最大长度为maxLength
中的$pSortTypes
属性。这用于正确的填充/对齐,也可以提高分类效率。
这演示了如何通过生成单一排序的用户定义的xsl:function
(在本例中为my:OrderedAttributeTuple()
)来解决最复杂的排序问题键。