группировать содержимое между двумя заданными именами элементов

Я немного борюсь с преобразованием XML-файла Filemaker во что-то более удобное для пользователя. FM по умолчанию сохраняет каждое предложение абзаца в элементе, и я бы хотел, чтобы все эти предложения были сгруппированы.

Ниже XML показывает, как это выглядит в необработанном виде:

<Para>
<ParaLine>
    <String>This is just some spacefiller, so some text to </String>
</ParaLine>
<ParaLine>
    <String>show how things look now. Go to </String>
    <XRef>
        <XRefName value="Heading"/>
    </XRef>
    <String>“</String>
    <String>More info here</String>
    <String>” </String>
</ParaLine>
<ParaLine>
    <String>(page</String>
    <Char value="HardSpace" type="enum"/>
    <String>27)</String>
    <XRefEnd/>
    <String>to get more details.</String>
</ParaLine>
</Para>

Моя цель двоякая: сначала я хотел бы получить все строковые значения в любой группе [para]. Этого относительно легко добиться с помощью следующего xlst:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" xmlns="http://www.w3.org/1999/xhtml" encoding="UTF-8" indent="yes"/>
<xsl:template match="@* | node()">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="text()">
    <xsl:value-of select="normalize-space(.)"/>
</xsl:template>

<xsl:template match="/">
    <xsl:result-document href="test.xml">
        <xsl:apply-templates/>
    </xsl:result-document>
</xsl:template>

<xsl:template match="String">
    <xsl:choose>
        <xsl:when test="preceding-sibling::*[1][name()='String']">
            <xsl:text> </xsl:text>
            <xsl:value-of select="text()"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="text()"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

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

<xsl:template match="Char[@value='HardSpace']">
    <xsl:text> </xsl:text>
</xsl:template>

</xsl:stylesheet>

Итак, мой текущий результат выглядит так:

<Para>This is just some spacefiller, so some text to show how things look now. Go to
 <XRef><XRefName value="Heading"/></XRef>
 “ More info here ” (page 27)
 <XRefEnd/>to get more details.
 </Para>

Однако моя вторая цель - получить содержимое между [XRef] и [XRefEnd] в одном теге, я могу сделать это с помощью пары дополнительных преобразований, но мне было интересно, возможно ли это за одну поездку. Моей главной «мечтой» было бы стать ниже XML за одну поездку:

<Para>
<local xml:lang="en">This is just some spacefiller, so some text to show how things look now. Go to 
<XRef XRefName="Heading">“ More info here ” (page 27)</XRef>
to get more details.</local>

Any tips on how I can do this with limited amount of transformations ?

Заранее спасибо !


person Wokoman    schedule 15.09.2011    source источник


Ответы (1)


Вот пример:

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

  <xsl:output indent="yes"/>

  <xsl:template match="Para">
    <xsl:copy>
      <xsl:for-each-group select="ParaLine/*" group-starting-with="XRef">
        <xsl:choose>
          <xsl:when test="self::XRef">
            <xsl:variable name="name" select="XRefName/@value"/>
            <xsl:for-each-group select="current-group() except ." group-ending-with="XRefEnd">
              <xsl:choose>
                <xsl:when test="position() eq 1">
                   <XRef XRefName="{$name}">
                     <xsl:apply-templates select="current-group()[position() ne last()]"/>
                   </XRef>
                 </xsl:when>
                 <xsl:otherwise>
                   <xsl:apply-templates select="current-group()"/>
                 </xsl:otherwise>
               </xsl:choose>
            </xsl:for-each-group>
          </xsl:when>
          <xsl:otherwise>
            <xsl:apply-templates select="current-group()"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Когда я использую Saxon 9.3 с приведенной выше таблицей стилей в опубликованном вами вводе, я получаю следующий результат:

<?xml version="1.0" encoding="UTF-8"?>
<Para>This is just some spacefiller, so some text to show how things look now. Go to <XRef XRefName="Heading">“More info here” (page27)</XRef>to get more details.</Para>
person Martin Honnen    schedule 15.09.2011
comment
Спасибо !!! Работает как шарм и еще раз доказал, что мне еще есть чему поучиться :-( Не могли бы вы дать несколько хороших предложений по книгам, которые стоит прочитать по этой теме? Большинство из них охватывают группировку, но не переходите в режим реального живого примера. Было бы здорово не только знать, как мне это нужно, но и понимать, зачем я это делаю ... - person Wokoman; 16.09.2011
comment
Если вы хотите изучить группировку с помощью XSLT 2.0, я думаю, что примеры группировки в спецификации XSLT 2.0 w3.org/TR/xslt20/#grouping-examples - полезный первый шаг, они охватывают различные атрибуты группировки, которые вы можете использовать с for-each-group, все с некоторыми примерами данных. Что касается книг, мне жаль, что я не знаю подробностей о том, как сгруппировать лечение в конкретных книгах, может быть, вы можете исследовать себя, заглянув в онлайн-книжные магазины в наши дни. - person Martin Honnen; 16.09.2011