Группировка XSLT 1.0 с использованием ключей на основе родительского атрибута

Используя XSLT 1.0, мне нужно преобразовать это:

<form>
<question NumOfColumns="3">
 <title>Colors</title> 
 <answer>red</answer> 
 <answer>orange</answer> 
 <answer>yellow</answer> 
 <answer>green</answer> 
 <answer>blue</answer> 
 <answer>indigo</answer> 
 <answer>violet</answer> 
</question>

</form>

в это:

<h2 class="question">Colors</h2>
<div class="answersrow">
<input type="checkbox" name="colors" value="red" id="red" /> <label for="red">red</label>
<input type="checkbox" name="colors" value="orange" id="orange" /> <label for="orange">orange</label>
<input type="checkbox" name="colors" value="yellow" id="yellow" /> <label for="yellow">yellow</label>
</div>
<div class="answersrow">
<input type="checkbox" name="colors" value="green" id="green" /> <label for="green">green</label>
<input type="checkbox" name="colors" value="blue" id="blue" /> <label for="blue">blue</label>
<input type="checkbox" name="colors" value="indigo" id="indigo" /> <label for="indigo">indigo</label>
</div>
<div class="answersrow">
<input type="checkbox" name="colors" value="green" id="green" /> <label for="green">green</label>
</div>

NumOfColumns в узле вопроса указывает, сколько столбцов использовать при выводе разделов ответов. Для каждого узла я могу получить его строку, используя:

потолок (позиция () div parent:: */@NumOfColumns)

Это работает нормально; я могу вывести правильное целое число. Но я не могу заставить работать ключи/группировку, и я не уверен, в чем проблема.

Я думал, что ключ будет:

<xsl:key name="answersrow" match="form/question/answer[ceiling( position() div parent::*/@NumOfColumns) = parent::*/@NumOfColumns]" use="." />

а затем я мог бы получить узлы с помощью:

<xsl:for-each select="key('answersrow', answer)">

Не повезло. У кого-нибудь есть решение? Или это невозможно в XSLT 1.0?


person devjeff    schedule 11.03.2012    source источник


Ответы (2)


И. XSLT 1.0: более простое, чем другие ответы (без параметров) и немного более короткое решение:

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

 <xsl:variable name="vNumCols" select="/*/*/@NumOfColumns"/>

 <xsl:template match="/*">
   <h2 class="question"><xsl:value-of select="*/title"/></h2>
   <xsl:apply-templates select=
        "*/answer[position() mod $vNumCols = 1]"/>
 </xsl:template>

 <xsl:template match="answer">
  <div class="answersrow">
   <xsl:apply-templates mode="inGroup" select=
    ". | following-sibling::*[not(position() >= $vNumCols)]"/>
  </div>
 </xsl:template>

 <xsl:template match="answer" mode="inGroup">
    <input type="checkbox" name="colors"
           value="{.}" id="{.}" />
    <label for="{.}"><xsl:value-of select="."/></label>
 </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к предоставленному XML-документу:

<form>
    <question NumOfColumns="3">
        <title>Colors</title>
        <answer>red</answer>
        <answer>orange</answer>
        <answer>yellow</answer>
        <answer>green</answer>
        <answer>blue</answer>
        <answer>indigo</answer>
        <answer>violet</answer>
    </question>
</form>

получен желаемый правильный результат:

<h2 class="question">Colors</h2>
<div class="answersrow">
   <input type="checkbox" name="colors" value="red" id="red"/>
   <label for="red">red</label>
   <input type="checkbox" name="colors" value="orange" id="orange"/>
   <label for="orange">orange</label>
   <input type="checkbox" name="colors" value="yellow" id="yellow"/>
   <label for="yellow">yellow</label>
</div>
<div class="answersrow">
   <input type="checkbox" name="colors" value="green" id="green"/>
   <label for="green">green</label>
   <input type="checkbox" name="colors" value="blue" id="blue"/>
   <label for="blue">blue</label>
   <input type="checkbox" name="colors" value="indigo" id="indigo"/>
   <label for="indigo">indigo</label>
</div>
<div class="answersrow">
   <input type="checkbox" name="colors" value="violet" id="violet"/>
   <label for="violet">violet</label>
</div>

II. Решение 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:strip-space elements="*"/>

 <xsl:variable name="vNumCols" select="/*/*/@NumOfColumns" as="xs:integer"/>

 <xsl:template match="/*">
   <h2 class="question"><xsl:value-of select="*/title"/></h2>

   <xsl:for-each-group select="*/answer"
        group-adjacent="(position() -1) idiv $vNumCols">
     <div class="answersrow">
      <xsl:apply-templates select="current-group()"/>
     </div>
   </xsl:for-each-group>
 </xsl:template>

 <xsl:template match="answer">
    <input type="checkbox" name="colors"
           value="{.}" id="{.}" />
    <label for="{.}"><xsl:value-of select="."/></label>
 </xsl:template>
</xsl:stylesheet>
person Dimitre Novatchev    schedule 11.03.2012

person    schedule
comment
Большое спасибо за ответы. Я должен был добавить в свой оригинал, что эта часть уже была внутри шаблона, поэтому мне пришлось сделать эту часть внутри именованного шаблона. @Dimitre, спасибо также за использование подхода XSLT 2.0. - person devjeff; 13.03.2012