Элементы вывода динамически основаны на условии в XSL? - PullRequest
2 голосов
/ 01 февраля 2012

Предположим, что следующий ввод 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 ]

2 голосов
/ 01 февраля 2012

I.Вот полное решение 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 голос
/ 01 февраля 2012

Если вы хотите, чтобы полученный xml всегда содержал предсказуемые элементы (в данном случае, последовательные годы, без пробелов, даже если в исходном документе есть пробелы в годах), почему бы вам просто не написать его (результирующий xml)?зачем преобразовывать в генерацию, если вы знаете, что это должно быть?

Я, возможно, немного преувеличиваю:)

, но вот идея: выберите самые высокие и самые низкие значения года, или сначала ипрошлой.затем переберите значения int от одного конца до другого.для каждой итерации цикла выполняется поиск соответствующего элемента в src xml.если найдено, сгенерируйте остальное, иначе пропустите.

...