обернуть узлы-братья на основе атрибута - PullRequest
2 голосов
/ 04 октября 2010

Используя XSLT, как я могу обернуть братьев и сестер, которые имеют одинаковое значение для атрибута.

Допустим, мне нужно обернуть один или несколько <amendment/> с <chapter/>, к которому они принадлежат.Из этого:

<section>
      <heading>some heading text</heading>
      <amendment num='1' chapter='1'>
            <foo/>
      </amendment>
      <amendment num='2' chapter='1'>
            <bar/>
      </amendment>
      <amendment num='3' chapter='2'>
            <baz/>
      </amendment>
      <heading>some heading text</heading>
      <amendment num='4' chapter='3'>
            <baz/>
      </amendment>
</section>

в это:

<section>
      <heading>some heading text</heading>
      <chapter num="1">
            <amendment num='1'>
                  <foo/>
            </amendment>
            <amendment num='2'>
                  <bar/>
            </amendment>
      </chapter>
      <chapter num="2">
            <amendment num='3'>
                  <baz/>
            </amendment>
      </chapter>
      <heading>some heading text</heading>
      <chapter num="3">
            <amendment num='4'>
                  <baz/>
            </amendment>
      </chapter>
</section>

Примечание 1: поправки всегда перечислены в порядке сортировки по главам в исходном XML.

Примечание 2: Я используюPHP5 с XSLT 1.0

Ответы [ 4 ]

1 голос
/ 04 октября 2010

Эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kAmendementByChapter" match="amendment" use="@chapter"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="amendment[count(.|key('kAmendementByChapter',
                                               @chapter)[1])=1]">
        <chapter num="{@chapter}">
            <xsl:apply-templates select="key('kAmendementByChapter',@chapter)"
                                 mode="copy"/>
        </chapter>
    </xsl:template>
    <xsl:template match="amendment"/>
    <xsl:template match="amendment" mode="copy">
        <xsl:call-template name="identity"/>
    </xsl:template>
    <xsl:template match="@chapter"/>
</xsl:stylesheet>

Вывод:

<section>
    <heading>some heading text</heading>
    <chapter num="1">
        <amendment num="1">
            <foo></foo>
        </amendment>
        <amendment num="2">
            <bar></bar>
        </amendment>
    </chapter>
    <chapter num="2">
        <amendment num="3">
            <baz></baz>
        </amendment>
    </chapter>
    <heading>some heading text</heading>
    <chapter num="3">
        <amendment num="4">
            <baz></baz>
        </amendment>
    </chapter>
</section>

Примечание : копировать все (правило идентификации), группируя по @ chapter.

0 голосов
/ 04 октября 2010

Спасибо @Alejandro и @ Flynn1179 за уведомление об ошибке в начальной версии этого решения - теперь исправлено!

Это преобразование :

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

    <xsl:key name="kbyChapter" match="amendment"
     use="@chapter"/>

 <xsl:template match="node()" name="identity">
     <xsl:copy>
       <xsl:apply-templates select="@*"/>
       <xsl:apply-templates select="node()[1]"/>
     </xsl:copy>
     <xsl:apply-templates select="following-sibling::node()[1]"/>
 </xsl:template>

 <xsl:template match="amendment"/>
 <xsl:template match=
 "amendment[not(@chapter=preceding-sibling::amendment[1]/@chapter)]">
  <chapter num="{@chapter}">
   <xsl:apply-templates select="key('kbyChapter',@chapter)" mode="copy"/>
  </chapter>
  <xsl:apply-templates select=
    "key('kbyChapter',@chapter)[last()]/following-sibling::node()[1]"/>
 </xsl:template>

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

 <xsl:template match="@*">
  <xsl:copy-of select="."/>
 </xsl:template>

 <xsl:template match="@chapter"/>
</xsl:stylesheet>

при применении к предоставленному документу XML (с исправлением в последнем amendment):

<section>
      <heading>some heading text</heading>
      <amendment num='1' chapter='1'>
            <foo/>
      </amendment>
      <amendment num='2' chapter='1'>
            <bar/>
      </amendment>
      <amendment num='3' chapter='2'>
            <baz/>
      </amendment>
      <heading>some heading text</heading>
      <amendment num='4' chapter='3'>
            <baz/>
      </amendment>
</section>

дает требуемый, правильный результат :

<section>
    <heading>some heading text</heading>
    <chapter num="1">
        <amendment num="1">
            <foo/>
        </amendment>
        <amendment num="2">
            <bar/>
        </amendment>
    </chapter>
    <chapter num="2">
        <amendment num="3">
            <baz/>
        </amendment>
    </chapter>
    <heading>some heading text</heading>
    <chapter num="3">
        <amendment num="4">
            <baz/>
        </amendment>
    </chapter>
</section>
0 голосов
/ 04 октября 2010

Поскольку ваши поправки отсортированы по главам, вы можете избежать использования ключа, вместо этого просто взглянув на непосредственно следующие и предшествующие элементы. Должно работать следующее:

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

<xsl:template match="amendment[not(@chapter = preceding-sibling::amendment[1]/@chapter)]">
  <chapter num="{@chapter}">
    <xsl:variable name="chapter" select="@chapter"/>
      <amendment num="{@num}">
        <xsl:apply-templates/>
      </amendment>
    <xsl:apply-templates select="following-sibling::amendment[1][@chapter = $chapter]">
      <xsl:with-param name="chapter" select="@chapter"/>
    </xsl:apply-templates>
  </chapter>
</xsl:template>

<xsl:template match="amendment">
  <xsl:param name="chapter"/>
  <xsl:if test="$chapter">
      <amendment num="{@num}">
        <xsl:apply-templates/>
      </amendment>
      <xsl:apply-templates select="following-sibling::amendment[1][@chapter = $chapter]">
        <xsl:with-param name="chapter" select="$chapter"/>
      </xsl:apply-templates>
  </xsl:if>
</xsl:template>

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

</xsl:stylesheet>
0 голосов
/ 04 октября 2010

Если вы используете XSLT 1, вы можете использовать метод группировки по Мюнхену следующим образом:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:key name="chapter" use="@chapter" match="amendment" />

  <xsl:template match="section">
    <xsl:copy>
      <xsl:apply-templates select="heading | amendment[generate-id() = generate-id(key('chapter',@chapter)[1])]" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="amendment">
    <xsl:element name="chapter">
      <xsl:attribute name="num">
        <xsl:value-of select="@chapter" />
      </xsl:attribute>
      <xsl:apply-templates select="key('chapter', @chapter)" mode="withoutchapter"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="amendment" mode="withoutchapter">
    <xsl:copy>
      <xsl:apply-templates  select="@*[(name() != 'chapter')] | node()"/>
    </xsl:copy>
  </xsl:template>

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

Здесь есть два «поправочных» шаблона - первый (без режима) вызывается толькошаблон раздела о поправках, которые являются первым появлением дополнения с этой главой.Он создает элемент главы и внутри него вызывает второй шаблон для каждого тега amendment с этой главой.

Два предостережения здесь;Во-первых, любые поправки без главы будут исключены из выходных данных.

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

Итак, если вы сделаете (сокращенно для ясности):

<amendment num='1' chapter='1' />
<heading>heading text</heading>
<amendment num='2' chapter='1' />

Будет выведено:

<chapter num='1'>
  <amendment num='1' />
  <amendment num='2' />
</chapter>
<heading>heading text</heading>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...