我正在进行一个由四个步骤组成的XSL-Transformation。我想出了各个步骤,但我仍然坚持如何将它们组合在一起。这就是我想要做的事情:
源XML文档:
<application>
<contactPerson>
<name>Dominik</name>
<countryCode>DE,SP</countryCode>
</contactPerson>
<contactPerson>
<name>Andrea</name>
<countryCode>FR</countryCode>
</contactPerson>
<contactPerson>
<name>Alex</name>
<countryCode>FR,DE</countryCode>
</contactPerson>
<contactPerson>
<name>Andre</name>
<countryCode>FR</countryCode>
</contactPerson>
</application>
目标XML文档:
<application>
<memberState>
<countryCode>DE</countryCode>
<contactPerson>
<name>Dominik</name>
</contactPerson>
<contactPerson>
<name>Dorothea</name>
</contactPerson>
</memberState>
<memberState>
<countryCode>FR</countryCode>
<contactPerson>
<name>Fiona</name>
</contactPerson>
<contactPerson>
<name>Fabian</name>
</contactPerson>
<contactPerson>
<name>Florian<name>
</contactPerson>
</memberState>
<memberState>
<countryCode>GB</countryCode>
<contactPerson>
<name>Gabi</name>
</contactPerson>
<contactPerson>
<name>Gert</name>
</contactPerson>
</memberState>
</application>
我确定了该流程的以下步骤:
现在我想出了如何做第1步:
<memberState>
<countryCodes>
<xsl:for-each select="/application/contactPerson">
<xsl:for-each select="tokenize(./countryCode, ',')">
<countryCode>
<xsl:value-of select="."/>
</countryCode>
</xsl:for-each>
</xsl:for-each>
</countryCodes>
</memberState>
对于第2步,我可以使用distinct-values()
。
对于第3步 + 第4步,我实施了以下解决方案:
<xsl:for-each select="/application/contactPerson/countryCode[not(. = ../preceding-sibling::*/countryCode)]">
<memberState>
<countryCode>
<xsl:value-of select="."/>
</countryCode>
<xsl:for-each select="/application/contactPerson[countryCode = current()]">
<contactPerson>
<name>
<xsl:value-of select="name"/>
</name>
</contactPerson>
</xsl:for-each>
</memberState>
</xsl:for-each>
但我怎样才能把所有东西都放在一起?我的想法是将每个步骤的输出保存在变量中,并在下一步中使用它,但我遇到的问题是XSLT中的变量是只读的。有没有办法以某种方式连接单个解决方案以获得所需的结果?
答案 0 :(得分:7)
我只是建议一步for-each-group
解决方案:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* , node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="application">
<xsl:copy>
<xsl:for-each-group select="contactPerson" group-by="tokenize(countryCode, ',')">
<memberState>
<countryCode><xsl:value-of select="current-grouping-key()"/></countryCode>
<xsl:apply-templates select="current-group()"/>
</memberState>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="contactPerson/countryCode"/>
</xsl:stylesheet>
当然有几个转换步骤是可能的,但是使用XSLT 2.0提供的for-each-group
等工具,我首先会考虑使用这些,而不是使用几个转换步骤。
如果你想使用distinct-values
,那当然是可能的;然而,我只将字符串值存储在变量中并对其进行操作,我不明白为什么使用XSLT 2.0你会想要一个临时树。所以这里有一个使用distinct-values
的示例和一个变量来存储它们以便在第二步中处理(我使用密钥来提高效率):
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="countryCodes" select="distinct-values(application/contactPerson/countryCode/tokenize(., ','))"/>
<xsl:variable name="main-input" select="/"/>
<xsl:key name="country" match="contactPerson" use="tokenize(countryCode, ',')"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* , node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="application">
<xsl:copy>
<xsl:for-each select="$countryCodes">
<memberState>
<countryCode><xsl:value-of select="."/></countryCode>
<xsl:apply-templates select="key('country', ., $main-input)"/>
</memberState>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="contactPerson/countryCode"/>
</xsl:stylesheet>
但我认为,在XSLT 2.0支持下,我的第一个建议更容易。