我有一个输入xml文件,如下所示
<FLEX_ATTRIBUTES>
<OBJ_ATTRIBUTE>
<ATTRIBUTE_NAME>FMGP_CLAIM_DESC</ATTRIBUTE_NAME>
<ATTRIBUTE_VALUE>Bull broke leg</ATTRIBUTE_VALUE>
</OBJ_ATTRIBUTE>
<OBJ_ATTRIBUTE>
<ATTRIBUTE_NAME>FMGP_CLAIM_TYPE</ATTRIBUTE_NAME>
<ATTRIBUTE_VALUE>Injury</ATTRIBUTE_VALUE>
</OBJ_ATTRIBUTE>
<OBJ_ATTRIBUTE>
<ATTRIBUTE_NAME>FMGP_CLAIM_STATUS</ATTRIBUTE_NAME>
<ATTRIBUTE_VALUE>Settled</ATTRIBUTE_VALUE>
</OBJ_ATTRIBUTE>
<OBJ_ATTRIBUTE>
<ATTRIBUTE_NAME>FMGP_YR_OF_CLAIM</ATTRIBUTE_NAME>
<ATTRIBUTE_VALUE>2009</ATTRIBUTE_VALUE>
</OBJ_ATTRIBUTE>
<OBJ_ATTRIBUTE>
<ATTRIBUTE_NAME>FMGP_CLAIM_AMOUNT</ATTRIBUTE_NAME>
<ATTRIBUTE_VALUE>7000</ATTRIBUTE_VALUE>
</OBJ_ATTRIBUTE>
<OBJ_ATTRIBUTE>
<ATTRIBUTE_NAME>FMGP_CLAIM_DESC</ATTRIBUTE_NAME>
<ATTRIBUTE_VALUE>Stolen tractor</ATTRIBUTE_VALUE>
</OBJ_ATTRIBUTE>
<OBJ_ATTRIBUTE>
<ATTRIBUTE_NAME>FMGP_CLAIM_TYPE</ATTRIBUTE_NAME>
<ATTRIBUTE_VALUE>theft</ATTRIBUTE_VALUE>
</OBJ_ATTRIBUTE>
<OBJ_ATTRIBUTE>
<ATTRIBUTE_NAME>FMGP_CLAIM_STATUS</ATTRIBUTE_NAME>
<ATTRIBUTE_VALUE>Settled</ATTRIBUTE_VALUE>
</OBJ_ATTRIBUTE>
<OBJ_ATTRIBUTE>
<ATTRIBUTE_NAME>FMGP_YR_OF_CLAIM</ATTRIBUTE_NAME>
<ATTRIBUTE_VALUE>2012/ATTRIBUTE_VALUE>
</OBJ_ATTRIBUTE>
<OBJ_ATTRIBUTE>
<ATTRIBUTE_NAME>FMGP_CLAIM_AMOUNT</ATTRIBUTE_NAME>
<ATTRIBUTE_VALUE>16000</ATTRIBUTE_VALUE>
</OBJ_ATTRIBUTE>
我希望得到以下格式的输出xml,其中每个声明都放入PreviousClaim元素。
<PreviousClaims>
<PreviousClaim>
<YrOfClaim>2009</YrOfClaim>
<ClaimType>Injury</ClaimType>
<ClaimDesc>Bull broke leg</ClaimDesc>
<ClaimStatus>Settled</ClaimStatus>
<ClaimAmount>7000</ClaimAmount>
</PreviousClaim>
<PreviousClaim>
<YrOfClaim>2012</YrOfClaim>
<ClaimType>Theft</ClaimType>
<ClaimDesc>Stolen tractor</ClaimDesc>
<ClaimStatus>Settled</ClaimStatus>
<ClaimAmount>17000</ClaimAmount>
</PreviousClaim>
在我的xsl中,我尝试使用for-each-group函数,如下所示,它将为每个发现的FMGP_CLAIM_DESC创建一个新的PreviousClaim元素,但是我在尝试获取其他元素时遇到了麻烦。
<PreviousClaims>
<xsl:for-each-group select="../OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_CLAIM_DESC']" group-by="ATTRIBUTE_VALUE">
<PreviousClaim>
<ClaimDesc><xsl:value-of select="ATTRIBUTE_VALUE"/></ClaimDesc>
</PreviousClaim>
</xsl:for-each-group>
</PreviousClaims>
我使用一些自定义构建的逻辑工作,但它不是非常优雅的解决方案,我希望利用XSLT 2.0中的一些新功能,使其更易于阅读和维护。
<PreviousClaims>
<xsl:for-each select="../OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_YR_OF_CLAIM']">
<PreviousClaim>
<YrOfClaim><xsl:value-of select="ATTRIBUTE_VALUE"/></YrOfClaim>
<!-- <position><xsl:value-of select="position()"/></position>-->
<xsl:variable name="pos" select="position()"></xsl:variable>
<xsl:for-each select="../OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_CLAIM_TYPE']">
<xsl:if test="position() = $pos">
<ClaimType><xsl:value-of select="ATTRIBUTE_VALUE"/></ClaimType>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="../OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_CLAIM_DESC']">
<xsl:if test="position() = $pos">
<ClaimDesc><xsl:value-of select="ATTRIBUTE_VALUE"/></ClaimDesc>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="../OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_CLAIM_STATUS']">
<xsl:if test="position() = $pos">
<ClaimStatus><xsl:value-of select="ATTRIBUTE_VALUE"/></ClaimStatus>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="../OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_CLAIM_AMOUNT']">
<xsl:if test="position() = $pos">
<ClaimAmount><xsl:value-of select="ATTRIBUTE_VALUE"/></ClaimAmount>
</xsl:if>
</xsl:for-each>
</PreviousClaim>
</xsl:for-each>
</PreviousClaims>
答案 0 :(得分:2)
使用group-starting-with
:
<xsl:template match="FLEX_ATTRIBUTES">
<Claims>
<xsl:for-each-group select="OBJ_ATTRIBUTE" group-starting-with="OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_CLAIM_DESC']">
<PreviousClaim>
<xsl:apply-templates select="current-group()"/>
</PreviousClaim>
</xsl:for-each-group>
</Claims>
</xsl:template>
<xsl:template match="OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_CLAIM_DESC']">
<ClaimDesc>
<xsl:value-of select="ATTRIBUTE_VALUE"/>
</ClaimDesc>
</xsl:template>
<xsl:template match="OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_CLAIM_TYPE']">
<ClaimType>
<xsl:value-of select="ATTRIBUTE_VALUE"/>
</ClaimType>
</xsl:template>
<!-- add similar templates here for the other attribute types -->
如果您需要更改订单,那么我仍然会使用分组和模板,但是您需要列出您想要的订单。
<xsl:template match="FLEX_ATTRIBUTES">
<Claims>
<xsl:for-each-group select="OBJ_ATTRIBUTE" group-starting-with="OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_CLAIM_DESC']">
<PreviousClaim>
<xsl:apply-templates select="current-group()[self::OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_YR_OF_CLAIM']], current-group()[self::OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_CLAIM_TYPE']], ., current-group()[self::OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_CLAIM_STATUS']], current-group()[self::OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_CLAIM_AMOUNT']]"/>
</PreviousClaim>
</xsl:for-each-group>
</Claims>
</xsl:template>
答案 1 :(得分:1)
下面是一个完整的XSLT样式表,它基于@MartinHonnen给出的答案。
<强>样式表强>
样式表只有一个OBJ_ATTRIBUTE
元素的通用模板。原则上,它也可以处理其他类型的属性 - 假设它们的结构是相同的。
元素名称不再包含_
并且已经过来的事实会使问题复杂化。
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="FLEX_ATTRIBUTES">
<PreviousClaims>
<xsl:for-each-group select="OBJ_ATTRIBUTE" group-starting-with="OBJ_ATTRIBUTE[ATTRIBUTE_NAME='FMGP_CLAIM_DESC']">
<PreviousClaim>
<xsl:apply-templates select="current-group()"/>
</PreviousClaim>
</xsl:for-each-group>
</PreviousClaims>
</xsl:template>
<xsl:template match="OBJ_ATTRIBUTE">
<xsl:variable name="step-1" select="tokenize(lower-case(substring-after(ATTRIBUTE_NAME,'FMGP')),'_')"/>
<xsl:variable name="step-2">
<xsl:for-each select="$step-1">
<xsl:value-of select="concat(upper-case(substring(.,1,1)),substring(., 2),' '[not(last())])"/>
</xsl:for-each>
</xsl:variable>
<xsl:element name="{$step-2}">
<xsl:value-of select="ATTRIBUTE_VALUE"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
<强>输出强>
请注意,元素的顺序与预期输出的顺序不同。如果订购对您很重要,那么我和Martin Honnen的回答都不会起作用。
<?xml version="1.0" encoding="UTF-8"?>
<PreviousClaims>
<PreviousClaim>
<ClaimDesc>Bull broke leg</ClaimDesc>
<ClaimType>Injury</ClaimType>
<ClaimStatus>Settled</ClaimStatus>
<YrOfClaim>2009</YrOfClaim>
<ClaimAmount>7000</ClaimAmount>
</PreviousClaim>
<PreviousClaim>
<ClaimDesc>Stolen tractor</ClaimDesc>
<ClaimType>theft</ClaimType>
<ClaimStatus>Settled</ClaimStatus>
<YrOfClaim>2012</YrOfClaim>
<ClaimAmount>16000</ClaimAmount>
</PreviousClaim>
</PreviousClaims>