2014-09-29 2 views
0

У меня есть следующий входной XML-код и задавался вопросом, может ли XSLT обрабатывать такое преобразование. Если это возможно, как это можно достичь?Повторение повторяющихся дочерних элементов в нерешающем родительском узле

входного XML:

<foo> 
    <bar> 
    <A>xxx</A> 
    <B>yyy</B> 
    <C>zzz</C> 
    <A>aaa</A> 
    <B>bbb</B> 
    <C>ccc</C> 
    ... 
    .. 
    </bar> 
</foo> 

вывода XML:

<data> 
    <A>xxx</A> 
    <B>yyy</B> 
    <C>zzz</C> 
</data> 
<data> 
    <A>aaa</A> 
    <B>bbb</B> 
    <C>ccc</C> 
</data> 
.... 

Там может быть более повторяющиеся узлы A, B, C в приведенном выше примере. Поскольку повторение не в повторяющемся родительском узле, его невозможно использовать для каждого. Я изучал возможность для каждой группы, но не уверен, что это применимо. Поблагодарили бы за консультацию.

+0

Это проблема * группировки * (выполнить поиск). Решения очень разные, если вы используете XSLT 1.0 или 2.0 - пожалуйста, выберите один (теги должны быть взаимоисключающими). –

ответ

0

Использование XSLT 1.0, следующее решение исправна - тестирование с помощью примера XML с 6 элементами:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="xml" indent="yes"/> 
<xsl:template match="/*"> 
<xsl:apply-templates select="/foo/bar"/> 
</xsl:template> 

<xsl:template match="bar"> 
<xsl:for-each select="*/node()"> 
    <xsl:if test="(position()-1) mod(3) = 0"> 
    <data> 
    <xsl:call-template name="grouping"> 
     <xsl:with-param name="position" select="position()"/> 
     <xsl:with-param name="amount" select="3"/> 
    </xsl:call-template> 
    </data> 
    </xsl:if> 
    </xsl:for-each> 
</xsl:template> 

<xsl:template name="grouping"> 
<xsl:param name="position" select="0"/> 
<xsl:param name="amount" select="0"/> 
<xsl:copy-of select="//bar/*[$position]"/> 
    <xsl:if test="$amount > $position or ($position mod(3) > 0)"> 
    <xsl:call-template name="grouping"> 
     <xsl:with-param name="position" select="$position + 1"/> 
     <xsl:with-param name="amount" select="$amount"/> 
    </xsl:call-template> 
    </xsl:if> 
</xsl:template> 
</xsl:stylesheet> 

Результат:

<?xml version="1.0" encoding="UTF-8"?> 
<data> 
    <A>xxx</A> 
    <B>yyy</B> 
    <C>zzz</C> 
</data> 
<data> 
    <A>aaa</A> 
    <B>bbb</B> 
    <C>ccc</C> 
</data> 

Как краткое объяснение - в "баре" соответствия шаблона все Элемент-узлы обрабатываются в

<xsl:for-each select="*/node()"> 

Использование <xsl:if test="(position()-1) mod(3) = 0"> (w hich равно 0 для 1-го узла и для каждого третьего узла), шаблон, называемый «группировка», вызывается для обеспечения групп генерации из 3 узлов. Этот шаблон вызывается с положением параметров - положением текущего узла - и количеством - количеством узлов, которые должны быть скопированы в каждой группе.
Шаблон группировки копирует текущий узел - <xsl:copy-of select="//bar/*[$position]"/> - проверяет, были ли уже созданы 3 узла - <xsl:if test="$amount > $position or ($position mod(3) > 0)"> - и снова вызывает себя, увеличивая счетчик в позиции, поэтому следующий узел будет скопирован.
На всякий случай это не очевидно - шаблон группировки будет вызван для $amount > $position, поэтому будут скопированы первые 3 узла и для $position mod(3) > 0 для всех следующих узлов, если он не кратен 3. В случае, если будет, например, 7-й элемент в вашем xml, это будет скопировано как один элемент в группе data.

0

Как вы изначально думали, xsl:for-each-group был бы идеальным; используйте атрибут group-starting-with.

XML-вход

<foo> 
    <bar> 
     <A>xxx</A> 
     <B>yyy</B> 
     <C>zzz</C> 
     <A>aaa</A> 
     <B>bbb</B> 
     <C>ccc</C> 
     <A>111</A> 
     <B>222</B> 
     <C>333</C> 
    </bar> 
</foo> 

XSLT 2,0

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

    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="/*"> 
     <xsl:for-each-group select="bar/*" group-starting-with="A"> 
      <data> 
       <xsl:apply-templates select="current-group()"/> 
      </data> 
     </xsl:for-each-group>  
    </xsl:template> 

</xsl:stylesheet> 

XML-выход (не хорошо сформированные в соответствии указано в первоначальном вопросе)

<data> 
    <A>xxx</A> 
    <B>yyy</B> 
    <C>zzz</C> 
</data> 
<data> 
    <A>aaa</A> 
    <B>bbb</B> 
    <C>ccc</C> 
</data> 
<data> 
    <A>111</A> 
    <B>222</B> 
    <C>333</C> 
</data> 
+0

Спасибо за ваш ввод. Мне удалось «убедить» команду вверх по течению, чтобы переупаковать данные для упрощения управления, поэтому у нисходящего приложения может быть легче справиться с этим. ;) –

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