Вставить родительский узел для группы узлов в XSLT

Мне нужно преобразовать с помощью XSLT вход XML:

<CONTAINER>container1</CONTAINER>
<STOP>stop1</STOP>
<PO>po1</PO>
<PO>po2</PO>
<PO>po3</PO>
<CONTAINER>container2</CONTAINER>
<STOP>stop2</STOP>
<PO>po4</PO>
<PO>po5</PO>
<PO>po6</PO>
<STOP>stop3</STOP>
<PO>po7</PO>
<PO>po8</PO>

в

<CONTAINER>container1</CONTAINER>
<STOP>
<new_tag>Collapsed STOP</new_tag>
<PO>po1</PO>
<PO>po2</PO>
<PO>po3</PO>
</STOP>
<CONTAINER>container2</CONTAINER>
<STOP>
<new_tag>Collapsed STOP</new_tag>
<PO>po4</PO>
<PO>po5</PO>
<PO>po6</PO>
<STOP>stop3</STOP>
<PO>po7</PO>
<PO>po8</PO>
</STOP>

Так что в основном мне нужно свернуть все теги POs за один STOP вместо того, чтобы иметь много STOP, каждый из которых имеет группу дочерних POs ad.

Может ли кто-нибудь помочь мне в этом? Я новичок в XSLT, поэтому не могу найти способ выполнить (если возможно) это преобразование.


person user5334634    schedule 14.09.2015    source источник
comment
Вы уже что-нибудь пробовали? Можете ли вы опубликовать свою таблицу стилей?   -  person potame    schedule 14.09.2015
comment
Какая версия XSLT?   -  person Daniel Haley    schedule 14.09.2015
comment
А как насчет 3-го STOP? В вашем выводе он не является родительским для POs. Какой-либо причине?   -  person Lingamurthy CS    schedule 15.09.2015


Ответы (2)


Предположения

  1. Ваш ввод должен быть хорошо оформленным документом. Я обернул его в элемент, чтобы это было так.
  2. Вы используете XSLT 2. Вы не указали версию.
  3. Каждый КОНТЕЙНЕР в документе всегда немедленно сопровождается СТОП.
  4. Первый дочерний элемент корневого элемента - КОНТЕЙНЕР.

Это преобразование XSLT 2 ...

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

<xsl:output indent="yes" encoding="utf-8" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />

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

<xsl:template match="t">
  <xsl:copy>
    <xsl:for-each-group select="*" group-starting-with="CONTAINER">
      <CONTAINER><xsl:value-of select="current-group()[self::CONTAINER]/text()" /></CONTAINER>
      <STOP>
        <new_tag>Collapsed STOP</new_tag>
        <xsl:apply-templates select="current-group()
          [not(self::CONTAINER)]      (: Excluded because we have already dealt with CONTAINER.  :)
          [position() ge 2     ]      (: Exclude the first STOP, because already dealt with.     :)
          " />
      </STOP>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

... преобразует этот входной документ ...

<t>
    <CONTAINER>container1</CONTAINER>
    <STOP>stop1</STOP>
    <PO>po1</PO>
    <PO>po2</PO>
    <PO>po3</PO>
    <CONTAINER>container2</CONTAINER>
    <STOP>stop2</STOP>
    <PO>po4</PO>
    <PO>po5</PO>
    <PO>po6</PO>
    <STOP>stop3</STOP>
    <PO>po7</PO>
    <PO>po8</PO>
</t>

... в этот вывод ...

<t>
   <CONTAINER>container1</CONTAINER>
   <STOP>
      <new_tag>Collapsed STOP</new_tag>
      <PO>po1</PO>
      <PO>po2</PO>
      <PO>po3</PO>
   </STOP>
   <CONTAINER>container2</CONTAINER>
   <STOP>
      <new_tag>Collapsed STOP</new_tag>
      <PO>po4</PO>
      <PO>po5</PO>
      <PO>po6</PO>
      <STOP>stop3</STOP>
      <PO>po7</PO>
      <PO>po8</PO>
   </STOP>
</t>

Обучающая записка

Обратитесь к моему ответу здесь, чтобы узнать больше об инструкции xsl: for-each-group.

person Sean B. Durkin    schedule 15.09.2015

Решение XSLT 1.0:

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

    <xsl:template match="/root">
        <root>
            <xsl:apply-templates />
        </root>

    </xsl:template>

    <xsl:template match="/root/CONTAINER">
        <xsl:copy-of select="."/>
        <xsl:apply-templates />
    </xsl:template>

    <xsl:template match="/root/STOP">
        <STOP>
            <new_tag><xsl:value-of select="." /></new_tag>
            <xsl:variable name="stopnum">
                <xsl:number level="single" count="STOP" />
            </xsl:variable>
            <xsl:for-each select="following-sibling::PO[count(preceding-sibling::STOP) = $stopnum]">
                <xsl:copy-of select="." />                
            </xsl:for-each>
        </STOP>
    </xsl:template>

    <xsl:template match="text()" />

</xsl:transform>

Ключевая часть здесь:

            <xsl:variable name="stopnum">
                <xsl:number level="single" count="STOP" />
            </xsl:variable>
            <xsl:for-each select="following-sibling::PO[count(preceding-sibling::STOP) = $stopnum]">

Мы устанавливаем переменную, которая сообщает нам положение последнего обработанного узла STOP, а затем выбираем все узлы PO в нашем for-each, которые имеют такое количество preceding-sibling, которые являются узлами STOP.

Обратите внимание, что я заключил ваш XML в узлы <root> и </root>, чтобы сделать его действительным XML.

Вход:

<root>
<CONTAINER>container1</CONTAINER>
<STOP>stop1</STOP>
<PO>po1</PO>
<PO>po2</PO>
<PO>po3</PO>
<CONTAINER>container2</CONTAINER>
<STOP>stop2</STOP>
<PO>po4</PO>
<PO>po5</PO>
<PO>po6</PO>
<STOP>stop3</STOP>
<PO>po7</PO>
<PO>po8</PO>
</root>

Выход:

<root>
   <CONTAINER>container1</CONTAINER>
   <STOP>
      <new_tag>stop1</new_tag>
      <PO>po1</PO>
      <PO>po2</PO>
      <PO>po3</PO>
   </STOP>
   <CONTAINER>container2</CONTAINER>
   <STOP>
      <new_tag>stop2</new_tag>
      <PO>po4</PO>
      <PO>po5</PO>
      <PO>po6</PO>
   </STOP>
   <STOP>
      <new_tag>stop3</new_tag>
      <PO>po7</PO>
      <PO>po8</PO>
   </STOP>
</root>
person Dan Field    schedule 15.09.2015