Как я могу заставить мой шаблон XSLT справляться с отсутствующими родительскими элементами? - PullRequest
0 голосов
/ 03 ноября 2010

Я хотел бы извиниться за плохое название - я действительно не знал, как это лучше сформулировать.В настоящее время я работаю над сценарием XSLT 1.0 (использующим xsltproc ), который преобразует простой формат XML в текстовое представление, пригодное для использования генератором PDF.

В моем формате XML естьвсего три элемента: <book>, <chapter> и <section>.Однако из-за некоторых неприятных особенностей DTD мне трудно написать правильный сценарий XSLT для преобразования документа.Вот DTD, описывающее их отношение:

<!ELEMENT book ((chapter|section)*)>
<!ELEMENT chapter (section*)>
<!ELEMENT section (#PCDATA)>

Вот моя таблица стилей XSLT, которая выполняет перевод:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="iso-8859-1"/>
<xsl:strip-space elements="*"/>

<xsl:template match="section">
  <xsl:if test="not(preceding-sibling::section)">@BeginSections&#xa;</xsl:if>
  <xsl:text>@Section @Begin&#xa;</xsl:text>
  <xsl:apply-templates/>
  <xsl:text>@End @Section&#xa;</xsl:text>
  <xsl:if test="not(following-sibling::section)">@EndSections&#xa;</xsl:if>
</xsl:template>

<xsl:template match="chapter">
  <xsl:if test="not(preceding-sibling::chapter)">@BeginChapters&#xa;</xsl:if>
  <xsl:text>@Chapter @Begin&#xa;</xsl:text>
  <xsl:apply-templates/>
  <xsl:text>@End @Chapter&#xa;</xsl:text>
  <xsl:if test="not(following-sibling::chapter)">@EndChapters&#xa;</xsl:if>
</xsl:template>

<xsl:template match="/book">
  <xsl:text>@Book @Begin&#xa;</xsl:text>
  <xsl:apply-templates/>
  <xsl:text>@End @Book&#xa;</xsl:text>
</xsl:template>
</xsl:stylesheet>

Теперь, здесь возникает сложная часть и мой вопрос: DTD делаетэлементы <section> могут иметь прямые дочерние элементы <book>.Тем не менее, мне все равно нужно выдать тот же вывод, как если бы этот элемент /book/section был на самом деле /book/chapter/section.

Так, например: <book><section/><chapter/></book> становится (я отступил для вывода для лучшей читаемости)

@Book @Begin
  @BeginChapters
  @Chapter @Begin
    @BeginSections
    @Section @Begin
    @End @Section
    @EndSections
  @End @Chapter
  @Chapter @Begin
  @End @Chapter
  @EndChapters
@End @Book

Итак, я хотел бы настроить мой XSLT-скрипт так, чтобы шаблон 'section' так или иначе вызывал шаблон 'chapter' в случае, если данный элемент <section> не находится внутри <chapter>.Как я мог это сделать?

Для чего это стоит, вот еще один пример.Несколько <section> элементов, которых еще нет в <chapter>, должны быть объединены в один.Следовательно, <book><section/><section/><section/><chapter/><section/></book> дает вывод для трех глав (первая из которых имеет три секции, вторая не имеет ни одной, третья имеет одну секцию).

Ответы [ 2 ]

2 голосов
/ 03 ноября 2010

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" encoding="iso-8859-1"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="chapter|section" mode="chapter" name="makeChapter">
        <xsl:text>  @Chapter @Begin&#xa;</xsl:text>
        <xsl:apply-templates select="self::chapter/node()[1]|
                                     self::section"
                             mode="section"/>
        <xsl:text>  @End @Chapter&#xa;</xsl:text>
        <xsl:apply-templates 
             select="self::chapter/following-sibling::node()[1]|
                     self::section/following-sibling::chapter[1] "
             mode="chapter"/>
    </xsl:template>
    <xsl:template match="section" mode="makeSection" name="makeSection">
        <xsl:text>    @Section @Begin&#xa;</xsl:text>
        <xsl:apply-templates/>
        <xsl:text>    @End @Section&#xa;</xsl:text>
        <xsl:apply-templates select="following-sibling::node()[1]/self::section"
                             mode="makeSection"/>
    </xsl:template>
    <xsl:template match="section" mode="section">
        <xsl:text>    @BeginSections&#xa;</xsl:text>
        <xsl:call-template name="makeSection"/>
        <xsl:text>    @EndSections&#xa;</xsl:text>
    </xsl:template>
    <xsl:template match="book/chapter|book/section">
        <xsl:text>  @BeginChapters&#xa;</xsl:text>
        <xsl:call-template name="makeChapter"/>
        <xsl:text>  @EndChapters&#xa;</xsl:text>
    </xsl:template>
    <xsl:template match="book">
        <xsl:text>@Book @Begin&#xa;</xsl:text>
        <xsl:apply-templates select="node()[1]"/>
        <xsl:text>@End @Book&#xa;</xsl:text>
    </xsl:template>
</xsl:stylesheet>

Выход:

@Book @Begin
  @BeginChapters
  @Chapter @Begin
    @BeginSections
    @Section @Begin
    @End @Section
    @EndSections
  @End @Chapter
  @Chapter @Begin
  @End @Chapter
  @EndChapters
@End @Book

Примечание : Мелкозернистый обход, объединение смежных книг / разделов в одну главу.

Редактировать : исправлено после родственного процесса для глав.

С этим входом:

<book>
    <section/>
    <section/>
    <section/>
    <chapter/>
    <section/>
</book>

Выход:

@Book @Begin
  @BeginChapters
  @Chapter @Begin
    @BeginSections
    @Section @Begin
    @End @Section
    @Section @Begin
    @End @Section
    @Section @Begin
    @End @Section
    @EndSections
  @End @Chapter
  @Chapter @Begin
  @End @Chapter
  @Chapter @Begin
    @BeginSections
    @Section @Begin
    @End @Section
    @EndSections
  @End @Chapter
  @EndChapters
@End @Book

Редактировать : лучше понимать именованные шаблоны.

Примечание : Пять правил: book правило "открывает" книгу и обрабатывает первого потомка; book/section|book/chapter правило (всегда первое, потому что мелкозернистая трансверсаль) «открывает» главы книги, называет makeChapter; Правило makeChapter, «открывает» главу, обрабатывает первый дочерний элемент, если контекст chapter, или self, если контекст section, оба в режиме section, обрабатывает следующий брат или сестру, если контекст chapter или следует chapter контекст section в режиме chapter (имеется в виду следующая глава); section правило в режиме section (поскольку процесс узел за узлом всегда будет совпадать с первым sections для смежных sections) "открывает" разделы раздела "правила вызовов" makeSection; makeSection Правило «открывает» раздел дочернего процесса, затем обрабатывает следующий брат section в режиме makeSection (это правило).

0 голосов
/ 03 ноября 2010

вам нужно сначала обернуть потерянные разделы в одну главу.

мы создадим для этого переменную для хранения обернутых элементов

<xsl:variable name="orphan">
  <chapter>
   <xsl:for-each select="/book/section">
      <xsl:copy-of select="." />
   </xsl:for-each>
  </chapter>
</xsl:variable>

, а затем при применении шаблонов внутри/book соответствующий шаблон, вам также нужно использовать только что созданную переменную

<xsl:apply-templates select="chapter|exslt:node-set($orphan)"/>

, а для использования exslt вам нужно добавить пространство имен

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

Finalрезультат

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common">
<xsl:output method="html" encoding="iso-8859-1"/>
<xsl:strip-space elements="*"/>

<xsl:variable name="orphan">
  <chapter>
   <xsl:for-each select="/book/section">
      <xsl:copy-of select="." />
   </xsl:for-each>
  </chapter>
</xsl:variable>

<xsl:template match="section">
  <xsl:if test="not(preceding-sibling::section)">@BeginSections&#xa;</xsl:if>
  <xsl:text>@Section @Begin&#xa;</xsl:text>
  <xsl:apply-templates/>
  <xsl:text>@End @Section&#xa;</xsl:text>
  <xsl:if test="not(following-sibling::section)">@EndSections&#xa;</xsl:if>
</xsl:template>

<xsl:template match="chapter">
  <xsl:if test="not(preceding-sibling::chapter)">@BeginChapters&#xa;</xsl:if>
  <xsl:text>@Chapter @Begin&#xa;</xsl:text>
  <xsl:apply-templates/>
  <xsl:text>@End @Chapter&#xa;</xsl:text>
  <xsl:if test="not(following-sibling::chapter)">@EndChapters&#xa;</xsl:if>
</xsl:template>

<xsl:template match="/book">
  <xsl:text>@Book @Begin&#xa;</xsl:text>
  <xsl:apply-templates select="chapter|exslt:node-set($orphan)"/>
  <xsl:text>@End @Book&#xa;</xsl:text>
</xsl:template>
</xsl:stylesheet>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...