当我以XML形式从SQL Server查询数据时,通常会生成重复的XML节点。我经常可以调整查询来消除这一点,但并不总是如此。对于那些我做不到的时候,我最终会使用这样的XML:
<Xml>
<House houseId="3" address="123 Main">
<Dog dogId="13" name="Rover">
<Flea fleaId="17" name="Chester" />
</Dog>
<Dog dogId="13" name="Rover">
<Flea fleaId="23" name="Poindexter" />
</Dog>
</House>
<House houseId="3" address="123 Main">
<Human humanId="9" name="Mr. Johnson">
<Child childId="11" name="Susie" />
</Human>
<Human humanId="9" name="Mr. Johnson">
<Child childId="31" name="Sandy" />
</Human>
</House>
<House houseId="5" address="987 Wall">
<Dog dogId="13" name="Rover">
<Flea fleaId="17" name="Chester" />
</Dog>
<Dog dogId="13" name="Rover">
<Flea fleaId="19" name="Wilhelm" />
</Dog>
</House>
</Xml>请注意,有两个相邻的节点,它们的属性相同。它们的不同之处仅在于它们的子节点。我正在尝试创建一个XSLT,它将采用相同的同级节点,并将它们折叠成一个包含所有子节点的超集的节点。在本例中,将同时包含和节点。如下所示:
<Xml>
<House houseId="3" address="123 Main">
<Dog dogId="13" name="Rover">
<Flea fleaId="17" name="Chester" />
<Flea fleaId="23" name="Poindexter" />
</Dog>
<Human humanId="9" name="Mr. Johnson">
<Child childId="11" name="Susie" />
<Child childId="31" name="Sandy" />
</Human>
</House>
<House houseId="5" address="987 Wall">
<Dog dogId="13" name="Rover">
<Flea fleaId="17" name="Chester" />
<Flea fleaId="19" name="Wilhelm" />
</Dog>
</House>
</Xml>不仅合并了两个相同的House节点,还合并了duplicate Dog和Human节点。但请注意,列出在两个不同节点下的节点并未组合,因为它们并不相同。(由于他们的祖先。)这就是我要做的:组合匹配的同级节点。
因为XML是由SQL生成的,所以XSLT将处理许多不同名称和排列的节点。因此,我不能对节点名称进行硬编码。但我可以肯定的是,每个节点都会有一个相应的id属性,其中包含一个数值。例如:、和。
我还知道根节点将没有属性,因此我可以开始处理根节点的子节点。
我的策略是为每个Node创建一个xsl:key,其中节点的key-value是其祖先节点与id值的串联。示例键值在下面的注释中
<Xml>
<House houseId="3" address="123 Main"><!--"houseId=3"-->
<Dog dogId="13" name="Rover" ><!--"houseId=3;dogId=13"-->
<Flea fleaId="17" name="Chester" /><!--"houseId=3;dogId=13;fleaId=17"-->
</Dog>
<Dog dogId="13" name="Rover" ><!--"houseId=3;dogId=13"-->
<Flea fleaId="23" name="Poindexter" /><!--"houseId=3;dogId=13;fleaId=23"-->
</Dog>
</House>
<House houseId="3" address="123 Main" ><!--"houseId=3"-->
<Human humanId="9" name="Mr. Johnson" ><!--"houseId=3;humanId=9"-->
<Child childId="11" name="Susie" /><!--"houseId=3;humanId=9;childId=11"-->
</Human>
<Human humanId="9" name="Mr. Johnson"><!--"houseId=3;humanId=9"-->
<Child childId="31" name="Sandy" /><!--"houseId=3;humanId=9;childId=31"-->
</Human>
</House>
<House houseId="5" address="987 Wall" ><!--"houseId=5"-->
<Dog dogId="13" name="Rover"><!--"houseId=5;dogId=13"-->
<Flea fleaId="17" name="Chester" /><!--"houseId=5;dogId=13;fleaId=17"-->
</Dog>
<Dog dogId="13" name="Rover"><!--"houseId=5;dogId=13"-->
<Flea fleaId="19" name="Wilhelm" /><!--"houseId=5;dogId=13;fleaId=19"-->
</Dog>
</House>
</Xml>因此,这两个看似匹配的事件将通过它们的祖先来区分:
houseId=3;dogId=13
与
houseId=5;dogId=13
这样,就可以组合重复的(同级)节点。不幸的是,我正在努力理解如何使用XSL和xslt:key来实现这一点。任何帮助都将不胜感激。
发布于 2013-01-02 11:41:01
此转换
<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">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*" priority="3">
<xsl:sequence select="my:grouping(., *)"/>
</xsl:template>
<xsl:function name="my:grouping" as="element()*">
<xsl:param name="pElem" as="element()"/>
<xsl:param name="pChildren" as="element()*"/>
<xsl:element name="{name($pElem)}" namespace="{namespace-uri($pElem)}">
<xsl:apply-templates select="$pElem/@*"/>
<xsl:for-each-group select="$pChildren" group-by="my:signature(.)">
<xsl:copy>
<xsl:apply-templates select="@*|node()[not(self::*)]"/>
<xsl:apply-templates select=
"my:grouping(., current-group()/*)/*"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:element>
</xsl:function>
<xsl:function name="my:signature" as="xs:string">
<xsl:param name="pElem" as="element()"/>
<xsl:variable name="vAttibs" as="xs:string*">
<xsl:perform-sort select="$pElem/@*">
<xsl:sort select="name()"/>
</xsl:perform-sort>
</xsl:variable>
<xsl:sequence select=
"string-join((name($pElem)
,for $at in $vAttibs
return concat($at, '+', $pElem/@*[name()=$at])
)
,'|')"/>
</xsl:function>
</xsl:stylesheet>在所提供的XML文档上应用时的:
<Xml>
<House houseId="3" address="123 Main">
<Dog dogId="13" name="Rover">
<Flea fleaId="17" name="Chester" />
</Dog>
<Dog dogId="13" name="Rover">
<Flea fleaId="23" name="Poindexter" />
</Dog>
</House>
<House houseId="3" address="123 Main">
<Human humanId="9" name="Mr. Johnson">
<Child childId="11" name="Susie" />
</Human>
<Human humanId="9" name="Mr. Johnson">
<Child childId="31" name="Sandy" />
</Human>
</House>
<House houseId="5" address="987 Wall">
<Dog dogId="13" name="Rover">
<Flea fleaId="17" name="Chester" />
</Dog>
<Dog dogId="13" name="Rover">
<Flea fleaId="19" name="Wilhelm" />
</Dog>
</House>
</Xml>会生成想要的正确结果:
<Xml>
<House houseId="3" address="123 Main">
<Dog dogId="13" name="Rover">
<Flea fleaId="17" name="Chester"/>
<Flea fleaId="23" name="Poindexter"/>
</Dog>
<Human humanId="9" name="Mr. Johnson">
<Child childId="11" name="Susie"/>
<Child childId="31" name="Sandy"/>
</Human>
</House>
<House houseId="5" address="987 Wall">
<Dog dogId="13" name="Rover">
<Flea fleaId="17" name="Chester"/>
<Flea fleaId="19" name="Wilhelm"/>
</Dog>
</House>
</Xml>和这个扩展的文档(添加了文本节点):
<Xml>
<House houseId="3" address="123 Main">
<Dog dogId="13" name="Rover">
Dog named Rover
<Flea fleaId="17" name="Chester">Regular dog flee</Flea>
</Dog>
<Dog dogId="13" name="Rover">
<Flea fleaId="23" name="Poindexter">Flea named Poindexter</Flea>
</Dog>
</House>
<House houseId="3" address="123 Main">
<Human humanId="9" name="Mr. Johnson">
<Child childId="11" name="Susie">Susan Johnson</Child>
</Human>
<Human humanId="9" name="Mr. Johnson">
<Child childId="31" name="Sandy">Sandy Johnson</Child>
</Human>
</House>
<House houseId="5" address="987 Wall">
<Dog dogId="13" name="Rover">
<Flea fleaId="17" name="Chester" />
</Dog>
<Dog dogId="13" name="Rover">
<Flea fleaId="19" name="Wilhelm" />
</Dog>
</House>
</Xml>再次生成正确的结果
<Xml>
<House houseId="3" address="123 Main">
<Dog dogId="13" name="Rover">
Dog named Rover
<Flea fleaId="17" name="Chester">Regular dog flee</Flea>
<Flea fleaId="23" name="Poindexter">Flea named Poindexter</Flea>
</Dog>
<Human humanId="9" name="Mr. Johnson">
<Child childId="11" name="Susie">Susan Johnson</Child>
<Child childId="31" name="Sandy">Sandy Johnson</Child>
</Human>
</House>
<House houseId="5" address="987 Wall">
<Dog dogId="13" name="Rover">
<Flea fleaId="17" name="Chester"/>
<Flea fleaId="19" name="Wilhelm"/>
</Dog>
</House>
</Xml>说明
我们使用两个函数:my:signature()和my:grouping()
my:signature()为每个元素创建一个签名--这是元素名称的竖线分隔字符串,所有按attrName.my:grouping()排序的attrName+value对都使用my:signature()进行正确的分组。它有第二个参数,包含要分组的元素。https://stackoverflow.com/questions/14116222
复制相似问题