2009-02-03 7 views
5

У меня есть простой XML с двумя уровнями (заголовок и линия) тегов, таких как:XSLT Группировка

<?xml version="1.0"?> 
<Header> 
    <line>Line 1</line> 
    <line>Line 2</line> 
    <line>Line 3</line> 
    <line>Line 4</line> 
    <line>Line 5</line> 
    <line>Line 6</line> 
    <line>Line 7</line> 
    <line>Line 8</line> 
    <line>Line 9</line> 
</Header> 

Мне нужно сгруппировать строки на множествах X (X = 3, например) линии так, что мой выход следующий:

<?xml version="1.0"?> 
<Header> 
    <set> 
     <line>Line 1</line> 
     <line>Line 2</line> 
     <line>Line 3</line> 
    </set> 
    <set> 
     <line>Line 4</line> 
     <line>Line 5</line> 
     <line>Line 6</line> 
    </set> 
    <set> 
     <line>Line 7</line> 
     <line>Line 8</line> 
     <line>Line 9</line> 
    </set> 
</Header> 

Как написать XSLT, который может сделать такое преобразование?

Спасибо!

O

+0

@otavio Вы, вероятно, с помощью багги, несовместимого XSLT процессора, или, что более вероятно, изменил XML исходного документа, или изменить код. - Dimitre Novatchev (0 секунд назад) –

ответ

4

Следующее преобразование производит требуемый результат:

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

<xsl:variable name="vN" select="3"/> 

    <xsl:template match="/*"> 
    <xsl:copy> 
     <xsl:apply-templates 
      select="line[position() mod $vN = 1]"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="line"> 
    <set> 
     <xsl:apply-templates mode="copy" select= 
     ". 
     | 
     following-sibling::line[position() &lt; $vN]"/> 
    </set> 
    </xsl:template> 

    <xsl:template match="line" mode="copy"> 
     <xsl:copy-of select="."/> 
    </xsl:template> 
</xsl:stylesheet> 

При нанесении на прилагаемом XML документа:

<Header> 
    <line>Line 1</line> 
    <line>Line 2</line> 
    <line>Line 3</line> 
    <line>Line 4</line> 
    <line>Line 5</line> 
    <line>Line 6</line> 
    <line>Line 7</line> 
    <line>Line 8</line> 
    <line>Line 9</line> 
</Header> 

результатом является:

<Header> 
    <set> 
    <line>Line 1</line> 
    <line>Line 2</line> 
    <line>Line 3</line> 
    </set> 
    <set> 
    <line>Line 4</line> 
    <line>Line 5</line> 
    <line>Line 6</line> 
    </set> 
    <set> 
    <line>Line 7</line> 
    <line>Line 8</line> 
    <line>Line 9</line> 
    </set> 
</Header> 

Обратите внимание следующее:

  1. Использование в XPath mod оператора выяснить первый line элемент в каждой группе vN элементов.

  2. Использование режимов для того, чтобы иметь возможность обрабатывать различные line элементы различных шаблонов

+0

он работал после того, как я изменил к Otavio

+0

Ты прав Димитрий! Я только что нашел свою ошибку и реализовал ту же концепцию в своей реальной проблеме, и она работает нормально. Еще раз спасибо! (Я удалю предыдущий комментарий) – Otavio

1

В целом в XSLT, если вы хотите создать иерархию из списка вы можете использовать предыдущий-собрат и после-одноуровневые ключевые слова. Это является упрощенным, если есть запись маркера между наборами.

Поскольку у вас нет маркера как такового, в данном случае я предполагаю, что решение может включать следующее ключевое слово sibling и оператор mod. Мод, обеспечивающий разделение между множествами.

Я не пробовал, но это был бы мой первый старт.

xslt, как правило, хорошее место, чтобы начать понимать различные ключевые слова.

0

Это должно быть возможно. Имеет желаемый результат:

<?xml version='1.0' encoding='UTF-8'?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
       version="1.0"> 

    <xsl:template match="/Header"> 
    <Header> 
     <xsl:for-each select="line"> 
     <xsl:if test="not(number()=0) and position() mod 3 = 0"> 
      <set> 
      <xsl:variable name="pos" select="position()"/> 
      <line><xsl:value-of select="../line[position()=($pos -2)]"/></line> 
      <line><xsl:value-of select="../line[position()=($pos -1)]"/></line> 
      <line><xsl:value-of select="text()"/></line> 
      </set> 
     </xsl:if> 

     </xsl:for-each> 
    </Header> 
    </xsl:template> 

</xsl:stylesheet> 

(The $ поз-1, $ поз-2 вещь не очень красиво)

1

http://www.xml.com/pub/a/2003/11/05/tr.html показывает чуть менее уродливый способ сделать это с помощью XSLT 2.0.Ключевой элемент является этим:

<xsl:for-each-group select="*" group-ending-with="*[position() mod 3 = 0]">

Смежные вопросы