XSLT组元素

时间:2014-08-12 10:34:42

标签: xml xslt

我有一个输入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>   

2 个答案:

答案 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>