我遇到了XML片段的XSLT转换问题。源XML看起来像这样:
<XXX>
<Name>Sample</Name>
<MMM>
<AAA ID="A"/>
<MMM>
<BBB ID="B"/>
<MMM>
<AA ID="C"/>
<BB ID="D"/>
</MMM>
</MMM>
</MMM>
</XXX>
但它需要转变为:
<XXX>
<Name>Sample</Name>
<MMM>
<MMM>
<MMM>
<AAA ID="A"/>
<BBB ID="B"/>
</MMM>
<AA ID="C"/>
</MMM>
<BB ID="D"/>
</MMM>
</XXX>
规则很简单,MMM元素只能有两个子元素节点。如果这些节点中只有一个碰巧是另一个MMM,则需要占据第一个位置。
使用代码很容易,但这些XML片段是SQL数据库中XML列的值,我想使用SQL和XSLT来更新这些值。
任何指针或建议?
答案 0 :(得分:1)
解决此问题的一种方法是首先提取MMM
树,将另一方面其他节点提取到单独的结构中,然后再将它们合并。
这真是一个很好的挑战!让我熬夜到凌晨4点。
以下XSLT(差不多!见下文)完成了这项工作:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0" exclude-result-prefixes="exslt">
<xsl:output method="xml" encoding="ISO-8859-1" />
<!-- handling of MMM extraction -->
<xsl:template match="MMM" mode="extract_mmm">
<MMM>
<xsl:apply-templates mode="extract_mmm" />
</MMM>
</xsl:template>
<xsl:template match="*" mode="extract_mmm" />
<!-- handling of extraction of other nodes -->
<xsl:template match="MMM" mode="extract_other">
<xsl:apply-templates mode="extract_other" />
</xsl:template>
<xsl:template match="*" mode="extract_other">
<xsl:copy-of select="." />
</xsl:template>
<!-- handling of merging the two partial result sets -->
<xsl:template match="MMM" mode="dump">
<xsl:param name="others" />
<xsl:choose>
<!-- this handles the case of an MMM being a leaf node -->
<xsl:when test="count(MMM) = 0">
<xsl:variable name="nodes_in_next_sibling" select="2*count(following-sibling::MMM)" />
<MMM>
<xsl:copy-of select="$others[count($others) - $nodes_in_next_sibling - 1]" />
<xsl:copy-of select="$others[count($others) - $nodes_in_next_sibling]" />
</MMM>
</xsl:when>
<!-- this handles the case of an inner MMM with a sibling -->
<xsl:when test="count(../MMM) = 2">
<xsl:variable name="free_positions_in_second_child" select="count(MMM[position() = 2 and count(MMM) = 0])*2 + count(MMM[2]//MMM[count(MMM) = 0])*2 + count(MMM[position() = 2 and count(MMM) = 1]) + count(MMM[2]//MMM[count(MMM) = 1])" />
<MMM>
<xsl:apply-templates mode="dump" select="MMM[1]">
<xsl:with-param name="others" select="$others[position() < count($others)- $free_positions_in_second_child + 1]" />
</xsl:apply-templates>
<xsl:apply-templates mode="dump" select="MMM[2]">
<xsl:with-param name="others" select="$others[position() >= count($others) - $free_positions_in_second_child + 1]" />
</xsl:apply-templates>
</MMM>
</xsl:when>
<!-- this handles the case of an inner MMM without sibling -->
<xsl:when test="count(../MMM) = 1">
<MMM>
<xsl:apply-templates mode="dump">
<xsl:with-param name="others" select="$others[position() < count($others)]" />
</xsl:apply-templates>
</MMM>
<xsl:copy-of select="$others[position() = count($others)]" />
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="XXX">
<XXX>
<xsl:copy-of select="Name" />
<xsl:variable name="mmm_structure">
<xsl:apply-templates mode="extract_mmm" select="MMM" />
</xsl:variable>
<xsl:variable name="other_structure_tmp">
<xsl:apply-templates mode="extract_other" select="MMM" />
</xsl:variable>
<!-- http://stackoverflow.com/questions/4610921/how-to-concatenate-two-node-sets-such-that-order-is-respected -->
<!-- http://www.exslt.org/exsl/ -->
<xsl:variable name="other_structure" select="exslt:node-set($other_structure_tmp/*)" />
<xsl:apply-templates select="$mmm_structure" mode="dump">
<xsl:with-param name="others" select="$other_structure" />
</xsl:apply-templates>
</XXX>
</xsl:template>
</xsl:stylesheet>
注意:
MMM
个节点。放置其他节点的规则是从底部到顶部填充位置。 这是输出:
<?xml version="1.0" encoding="ISO-8859-1"?>
<XXX>
<Name>Sample</Name>
<MMM>
<MMM>
<MMM>
<AAA ID="A"/>
<AAA ID="B"/>
</MMM>
</MMM>
<BBB ID="C"/>
</MMM>
<BBB ID="D"/>
</XXX>
如果这是一个问题,请告诉我。