Объединение смежных узлов одного типа (XSLT 1.0) - PullRequest
1 голос
/ 27 ноября 2009

Можно ли объединить каждую последовательность узлов одного и того же указанного типа? (в данном случае 'aaa') (не только первое вхождение последовательности)

Вот мой ввод XML:

<block>
    <aaa>text1</aaa>
    <aaa>text2</aaa>
    <aaa><xxx>text3</xxx></aaa>
    <bbb>text4</bbb>
    <aaa>text5</aaa>
    <bbb><yyy>text6</yyy></bbb>
    <bbb>text7</bbb>
    <aaa>text8</aaa>
    <aaa><zzz>text9</zzz></aaa>
    <aaa>texta</aaa>
</block>

И я хочу следующий вывод:

<block>
    <aaa>text1text2<xxx>text3</xxx></aaa>
    <bbb>text4</bbb>
    <aaa>text5</aaa>
    <bbb><yyy>text6</yyy></bbb>
    <bbb>text7</bbb>
    <aaa>text8<zzz>text9</zzz>texta</aaa>
</block>

Любая помощь приветствуется

Ответы [ 3 ]

0 голосов
/ 27 ноября 2009

Вот еще один способ сделать это.

Сначала сопоставьте все дочерние узлы элемента блока

<xsl:template match="block/child::*">

Далее, проверьте, имеет ли другое имя самого прямого родственного элемента другое имя, указывая, что это первый из одного или нескольких соседних элементов:

<xsl:if test="local-name(preceding-sibling::*[position()=1]) != $name">

Если это так, вы можете скопировать этот узел. Затем вам нужно скопировать следующих братьев и сестер с тем же именем. Я сделал это, рекурсивно вызывая шаблон для каждого сразу же следующего брата с тем же именем

<xsl:apply-templates select="following-sibling::*[1][local-name()=$name]" mode="next"/>

Соединение всего этого дает

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

   <!-- Match children of the block element -->
   <xsl:template match="block/child::*">
      <xsl:variable name="name" select="local-name()"/>

      <!-- Is this the first element in a sequence? -->
      <xsl:if test="local-name(preceding-sibling::*[position()=1]) != $name">
         <xsl:copy>
            <xsl:apply-templates />

            <!-- Match the next sibling if it has the same name -->
            <xsl:apply-templates select="following-sibling::*[1][local-name()=$name]" mode="next"/>
         </xsl:copy>
      </xsl:if>
   </xsl:template>

   <!-- Recursive template used to match the next sibling if it has the same name -->
   <xsl:template match="block/child::*" mode="next">
      <xsl:variable name="name" select="local-name()"/>
         <xsl:apply-templates />
      <xsl:apply-templates select="following-sibling::*[1][local-name()=$name]" mode="next"/>
   </xsl:template>

   <!-- Template used to copy a generic node -->
   <xsl:template match="@* | node()">
         <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
         </xsl:copy>
   </xsl:template>
</xsl:stylesheet>
0 голосов
/ 28 ноября 2009

Вот другой подход, без использования рекурсивных шаблонов.

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

  <xsl:template match="aaa">
    <xsl:if test="not(preceding-sibling::*[1]/self::aaa)">
      <xsl:variable name="following" 
                    select="following-sibling::aaa[
                              not(preceding-sibling::*[
                                not(self::aaa) and
                                not(following-sibling::aaa = current())
                              ])
                            ]"/>
      <xsl:copy>
        <xsl:apply-templates select="$following/@*"/>
        <xsl:apply-templates select="@*"/>
        <xsl:apply-templates select="node()"/>
        <xsl:apply-templates select="$following/node()"/>
      </xsl:copy>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

Довольно запутанное выражение XPath для выбора следующих одноуровневых aaa узлов, которые объединяются с текущим:

following-sibling::aaa[                       # following 'aaa' siblings
  not(preceding-sibling::*[                   #   if they are not preceded by
    not(self::aaa) and                        #     a non-'aaa' node
    not(following-sibling::aaa = current())   #     after the current node
  ])
]
0 голосов
/ 27 ноября 2009

При условии, что у вас есть только один block, метод Мюнхена - наиболее оптимизированный способ сделать это:

<!-- group nodes by name -->
<xsl:key name="block-children-by-name" match="block/*" use="name()"/>

<!-- for nodes that aren't first in their group, no output -->
<xsl:template match="block/*" />

<!-- for nodes that are first in their group, combine group children and output -->
<xsl:template match="block/*[generate-id() =
                             generate-id(key('block-children-by-name', name())[1])]">
   <xsl:copy>
     <xsl:copy-of select="key('block-children-by-name', name())/*"/>
   </xsl:copy>
</xsl:template>

Обратите внимание, что это объединяет только дочерние узлы, а не, например, любые атрибуты, которые могут встречаться на самих aaa и bbb.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...