我有多种类型的xml消息我需要通过在同一父节点下对多个节点进行分组来“压缩”(相同的父节点意味着它们共享相同的节点名称,并且声明的每个属性也相等)。例如:
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012">
<RatingByNumber Code="X" Rating="10" Number="1">
<RatingByNumber Code="X" Rating="19" Number="2">
</Rating>
</Ratings>
</TopLevel>
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1">
<RatingByNumber Code="X" Rating="19" Number="2">
</Rating>
</Ratings>
</TopLevel>
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1">
<RatingByNumber Code="X" Rating="19" Number="2">
</Rating>
</Ratings>
</TopLevel>
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="30" Number="3">
<RatingByNumber Code="X" Rating="39" Number="4">
</Rating>
</Ratings>
</TopLevel>
注意它们如何共享相同的CodeTL属性,后两个共享相同的CodeA,Start和End属性,所以我需要的是使用xslt生成以下输出
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012">
<RatingByNumber Code="X" Rating="10" Number="1">
<RatingByNumber Code="X" Rating="19" Number="2">
</Rating>
<Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1">
<RatingByNumber Code="X" Rating="19" Number="2">
</Rating>
<Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1">
<RatingByNumber Code="X" Rating="19" Number="2">
<RatingByNumber Code="X" Rating="30" Number="3">
<RatingByNumber Code="X" Rating="39" Number="4">
</Rating>
</Ratings>
</TopLevel>
更干净,根据使用它的应用程序,它可以节省处理时间并节省空间。
我遇到的问题是我有不同类型的xml消息,它们具有不同的节点名称和属性(以及属性数量),但它们都共享我在此处显示的相同结构。 处理所有这些都是一种通用的方法,但我很感激XSLT可以转换我提供的示例,因此我可以为每个需要发送的xml消息创建自定义代码。
答案 0 :(得分:1)
此XSLT 1.0样式表产生了所需的结果:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="byCodeTL" match="TopLevel" use="@CodeTL"/>
<xsl:key name="byAttrs" match="Rating"
use="concat(../../@CodeTL, '|', @CodeA, '|', @Start, '|', @End)"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="TopLevel[generate-id()=
generate-id(key('byCodeTL', @CodeTL)[1])]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<Ratings>
<xsl:apply-templates
select="key('byCodeTL', @CodeTL)/Ratings/*"/>
</Ratings>
</xsl:copy>
</xsl:template>
<xsl:template match="Rating[generate-id()=
generate-id(key('byAttrs',
concat(../../@CodeTL, '|', @CodeA, '|', @Start, '|', @End))[1])]">
<xsl:copy>
<xsl:apply-templates select="@*|key('byAttrs',
concat(../../@CodeTL, '|', @CodeA, '|', @Start, '|', @End))/*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="TopLevel"/>
<xsl:template match="Rating"/>
</xsl:stylesheet>
所有TopLevel
元素都按其CodeTL
属性进行分组。所有Rating
元素都按其属性和相应CodeTL
的{{1}}属性的组合进行分组。
答案 1 :(得分:1)
此通用XSLT 2.0转换:
<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="xs my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<t>
<xsl:sequence select="my:grouping(*)"/>
</t>
</xsl:template>
<xsl:function name="my:grouping" as="node()*">
<xsl:param name="pElems" as="element()*"/>
<xsl:if test="$pElems">
<xsl:for-each-group select="$pElems" group-by="my:signature(.)">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:sequence select="my:grouping(current-group()/*)"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:if>
</xsl:function>
<xsl:function name="my:signature" as="xs:string">
<xsl:param name="pElem" as="element()"/>
<xsl:variable name="vsignAttribs" as="xs:string*">
<xsl:for-each select="$pElem/@*">
<xsl:sort select="name()"/>
<xsl:value-of select="concat(name(), '=', .,'|')"/>
</xsl:for-each>
</xsl:variable>
<xsl:sequence select=
"concat(name($pElem), '|', string-join($vsignAttribs, ''))"/>
</xsl:function>
</xsl:stylesheet>
应用于提供的XML (包装到单个顶部元素中以成为格式良好的XML文档):
<t>
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
</Rating>
</Ratings>
</TopLevel>
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
</Rating>
</Ratings>
</TopLevel>
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
</Rating>
</Ratings>
</TopLevel>
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="30" Number="3"/>
<RatingByNumber Code="X" Rating="39" Number="4"/>
</Rating>
</Ratings>
</TopLevel>
</t>
生成想要的正确结果:
<t>
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
</Rating>
<Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
</Rating>
<Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
<RatingByNumber Code="X" Rating="30" Number="3"/>
<RatingByNumber Code="X" Rating="39" Number="4"/>
</Rating>
</Ratings>
</TopLevel>
</t>
<强>解释强>:
执行的分组在函数my:grouping()
中实现,并且是递归的。
顶级元素在其级别上是单个元素,不需要任何其他分组,而只需要自身的浅层副本。然后在这个浅层副本的主体内部,通过函数my:grouping()
执行较低级别的分组。
函数my:grouping()
有一个参数,它是直接上一级组中所有元素的所有子元素。它返回当前级别的所有组。
作为参数传递给函数的元素序列根据其签名进行分组 - 元素名称与其属性的所有名称 - 值对的串联它们的相应值,并使用适当的分隔符分隔。元素的签名由函数my:signature()
生成。
<强> II。通用XSLT 1.0解决方案:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
xmlns:my="my:my" exclude-result-prefixes="my ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vrtfPass1">
<xsl:apply-templates select="/*"/>
</xsl:variable>
<xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)"/>
<xsl:template match="/">
<xsl:apply-templates select="$vPass1/*" mode="pass2"/>
</xsl:template>
<xsl:template match="/*" mode="pass2">
<xsl:copy>
<xsl:call-template name="my:grouping">
<xsl:with-param name="pElems" select="*"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template name="my:grouping">
<xsl:param name="pElems" select="/.."/>
<xsl:if test="$pElems">
<xsl:for-each select="$pElems">
<xsl:variable name="vPos" select="position()"/>
<xsl:if test=
"not(current()/@my:sign
= $pElems[not(position() >= $vPos)]/@my:sign
)">
<xsl:element name="{name()}">
<xsl:copy-of select="namespace::*[not(. = 'my:my')]"/>
<xsl:copy-of select="@*[not(name()='my:sign')]"/>
<xsl:call-template name="my:grouping">
<xsl:with-param name="pElems" select=
"$pElems[@my:sign = current()/@my:sign]/*"/>
</xsl:call-template>
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:if>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="*/*">
<xsl:variable name="vSignature">
<xsl:call-template name="signature"/>
</xsl:variable>
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:attribute name="my:sign">
<xsl:value-of select="$vSignature"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template name="signature">
<xsl:variable name="vsignAttribs">
<xsl:for-each select="@*">
<xsl:sort select="name()"/>
<xsl:value-of select="concat(name(), '=', .,'|')"/>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select=
"concat(name(), '|', $vsignAttribs)"/>
</xsl:template>
</xsl:stylesheet>
当在同一个XML文档(上面)上应用此转换时,会再次生成相同的正确结果:
<t>
<TopLevel>
<Ratings>
<Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
</Rating>
<Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
</Rating>
<Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
<RatingByNumber Code="X" Rating="30" Number="3"/>
<RatingByNumber Code="X" Rating="39" Number="4"/>
</Rating>
</Ratings>
</TopLevel>
</t>
<强>解释强>:
这是两次转换。
在每个元素的第一遍中计算签名,它就成为新属性my:sign
的valye。
使用与XSLT 2.0解决方案相同的递归分组算法。