根据XSL中的条件动态输出元素?

时间:2012-02-01 01:38:53

标签: xml xslt transform xslt-2.0

假设以下xml输入...

<incidents>
       <incident>
              <year>2011</year>
              <other data here>
       </incident>
       <incident>
              <year>2009</year>
              <other data here>
       </incident>
       <incident>
              <year>2006</year>
       </incident>
</incidents>

xml总是按年分类,因此最新的事件年是第一个。我需要使用xsl处理它,并且基本上使用最小变换输出数据5年,最多,但是如果缺少任何年份,我只需要为<incident><year>missingYear</year></incident>输出一个元素。

因此,假设我有正确的XSL来执行此操作,处理上述xml将产生此...

<incidents>
   <incident>
      <year>2011</year>
   </incident>
   <incident>
      <year>2010</year>
   </incident>
   <incident>
      <year>2009</year>
   </incident>
   <incident>
      <year>2008</year>
   </incident>
   <incident>
      <year>2007</year>
   </incident>
   <incident>
      <year>2006</year>
   </incident>
</incidents>

我用xsl得到了这么多,但它并没有考虑到多年之间的巨大差距

<xsl:variable name="maxYear" select="/incidents/incident/year[1]"></xsl:variable>

<xsl:template match="incidents" >
  <xsl:element name="incident">           
    <xsl:for-each select="incident">                
      <xsl:variable name="currentYear" select="year"/>              

         <xsl:choose>
           <xsl:when test="($maxYear - (position() -1)) != $currentYear">
             <!-- output the missing year -->
             <xsl:element name="year"> <xsl:value-of select="($maxYear - (position() -1))" /></xsl:element>
             <!-- output the current year node -->
             <xsl:element name="year"> <xsl:value-of select="$currentYear" /></xsl:element>
            </xsl:when>
            <xsl:otherwise>
              <xsl:element name="year"> <xsl:value-of select="$currentYear" /></xsl:element>             
            </xsl:otherwise>           
         </xsl:choose>                     
    </xsl:for-each>       
  </xsl:element>
</xsl:template>

2 个答案:

答案 0 :(得分:2)

<强>予。这是一个完整的XSLT 1.0解决方案:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pYearsBack" select="6"/>
 <xsl:param name="pThisYear" select="2012"/>

 <xsl:variable name="vEarliest" select=
  "$pThisYear - $pYearsBack"/>

 <xsl:variable name="vYears" select="/*/*/year"/>

 <xsl:template match="/">
  <incidents>
    <xsl:call-template name="genYears"/>
  </incidents>
 </xsl:template>

 <xsl:template name="genYears">
  <xsl:param name="pTimes" select="$pYearsBack+1"/>
  <xsl:param name="pStart" select="$pThisYear"/>

  <xsl:if test="$pTimes > 0">
   <incident>
     <year>
      <xsl:value-of select=
      "concat($vYears[. = $pStart],
              substring('missingYear',
                         1 div not($vYears[. = $pStart]))
             )
      "/>
     </year>
   </incident>

   <xsl:call-template name="genYears">
    <xsl:with-param name="pTimes" select="$pTimes -1"/>
    <xsl:with-param name="pStart" select="$pStart -1"/>
   </xsl:call-template>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

将此转换应用于提供的XML文档(更正为格式良好):

<incidents>
    <incident>
        <year>2011</year>
        <other-data-here/>
    </incident>
    <incident>
        <year>2009</year>
        <other-data-here/>
    </incident>
    <incident>
        <year>2006</year>
    </incident>
</incidents>

想要的,正确的结果(从$pThisYear返回$pYearsBack年开始的所有年度事件)生成

<incidents>
   <incident>
      <year>missingYear</year>
   </incident>
   <incident>
      <year>2011</year>
   </incident>
   <incident>
      <year>missingYear</year>
   </incident>
   <incident>
      <year>2009</year>
   </incident>
   <incident>
      <year>missingYear</year>
   </incident>
   <incident>
      <year>missingYear</year>
   </incident>
   <incident>
      <year>2006</year>
   </incident>
</incidents>

<强> II。 XSLT 2.0解决方案:

与往常一样,XSLT 2.0解决方案更容易,更简洁,更易读:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 exclude-result-prefixes="xs">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="pYearsBack" select="6" as="xs:integer"/>
 <xsl:param name="pThisYear" select="2012" as="xs:integer"/>

 <xsl:variable name="vEarliest" select=
  "$pThisYear - $pYearsBack -1"/>

 <xsl:variable name="vYears" select="/*/*/year/xs:integer(.)"/>

 <xsl:template match="/">
  <incidents>
   <xsl:for-each select="1 to $pYearsBack +1">
    <xsl:variable name="vthisYear" as="xs:integer"
         select="$pThisYear - . +1"/>
    <incident>
      <year>
       <xsl:sequence select=
       "($vYears[. eq $vthisYear], 'missingYear')[1]"/>
      </year>
    </incident>
   </xsl:for-each>
   </incidents>
 </xsl:template>
</xsl:stylesheet>

将此转换应用于同一XML文档(上图)时,会生成相同的正确结果

<incidents>
   <incident>
      <year>missingYear</year>
   </incident>
   <incident>
      <year>2011</year>
   </incident>
   <incident>
      <year>missingYear</year>
   </incident>
   <incident>
      <year>2009</year>
   </incident>
   <incident>
      <year>missingYear</year>
   </incident>
   <incident>
      <year>missingYear</year>
   </incident>
   <incident>
      <year>2006</year>
   </incident>
</incidents>

答案 1 :(得分:1)

如果你希望得到的xml总是包含可预测的元素(在这种情况下,连续几年,没有间隙,即使源文档有多年的空白),那么为什么不编写它(生成的xml)?为什么转换为生成,如果你知道它需要什么?

我可能会夸大一点:)

但这是一个想法:选择最高和最低年份值,或者第一个和最后一个。然后将int值从一端循环到另一端。对于每个循环迭代,查找src xml中的匹配元素。如果找到,则生成其余部分,否则跳过。