Используйте XSLT 1.0, чтобы сгруппировать элементы XML в сегменты по порядку, основываясь на некоторых критериях - PullRequest
3 голосов
/ 23 июня 2011

Скажем, у меня был какой-то XML, который я хотел преобразовать в HTML. XML разделен на упорядоченные разделы:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <section attr="someCriteria">
    <h1>Title 1</h1>
    <p>paragraph 1-1</p>
    <p>paragraph 1-2</p>
  </section>
  <section attr="someOtherCriteria">
    <h3>Subtitle 2</h3>
    <ul>
      <li>list item 2-1</li>
      <li>list item 2-2</li>
      <li>list item 2-3</li>
      <li>list item 2-4</li>
    </ul>
  </section>
  <section attr="anotherSetOfCriteria">
    <warning>
      Warning: This product could kill you
    </warning>
  </section>
  <section attr="evenMoreCriteria">
    <disclaimer>
      You were warned
    </disclaimer>
  </section>
  <section attr="criteriaSupreme">
    <p>Copyright 1999-2011</p>
  </section>
</root>

У меня есть несколько таких XML-документов. Мне нужно сгруппировать и преобразовать эти разделы на основе критериев. Там будет два разных вида ведер.

  • Итак, первый раздел пойдет в ведро (например, <div class="FormatOne"></div>)
  • Если второй раздел соответствует критериям претендовать на ведро "FormatOne" это также войдет в это ведро
  • Если третий раздел требует другого ведро (например, <div class="FormatTwo"></div>), затем новый ведро создано и раздел содержимое помещено в это ведро
  • Если корзина для четвертого раздела требует «FormatOne» (который отличается от предыдущего формата), тогда новый ведро создается снова и раздел содержимое помещено в это ведро
  • и т.д.. Каждый раздел будет идти в том же сегменте, что и предыдущий раздел, если они имеют одинаковый формат. Если нет, то создается новое ведро.

Таким образом, для каждого документа, в зависимости от логики разделения сегментов, документ может выглядеть так:

<body>
  <div class="FormatOne">
    <h1>Title 1</h1>
    <p>paragraph 1-1</p>
    <p>paragraph 1-2</p>
    <h3>Subtitle 2</h3>
    <ul>
      <li>list item 2-1</li>
      <li>list item 2-2</li>
      <li>list item 2-3</li>
      <li>list item 2-4</li>
    </ul>
  </div>
  <div class="FormatTwo">
    <span class="warningText">
      Warning: This product could kill you
    </span>
  </div>
  <div class="FormatOne">
    <span class="disclaimerText"> You were warned</span>
    <p class="copyright">Copyright 1999-2011</p>
  </div>
</body>

это:

<body>
  <div class="FormatOne">
    <h1>Title 1</h1>
    <p>paragraph 1-1</p>
    <p>paragraph 1-2</p>
    <h3>Subtitle 2</h3>
  </div>
  <div class="FormatTwo">
    <ul>
      <li>list item 2-1</li>
      <li>list item 2-2</li>
      <li>list item 2-3</li>
      <li>list item 2-4</li>
    </ul>
  </div>
  <div class="FormatOne">
    <span class="warningText">
      Warning: This product could kill you
    </span>
    <span class="disclaimerText"> You were warned</span>
    <p class="copyright">Copyright 1999-2011</p>
  </div>
</body>

или даже это:

<body>
  <div class="FormatOne">
    <h1>Title 1</h1>
    <p>paragraph 1-1</p>
    <p>paragraph 1-2</p>
    <h3>Subtitle 2</h3>
    <ul>
      <li>list item 2-1</li>
      <li>list item 2-2</li>
      <li>list item 2-3</li>
      <li>list item 2-4</li>
    </ul>
    <span class="warningText">
      Warning: This product could kill you
    </span>
    <span class="disclaimerText"> You were warned</span>
    <p class="copyright">Copyright 1999-2011</p>
  </div>
</body>

в зависимости от того, как определены разделы.

Есть ли способ использовать XSLT для выполнения этого типа магии группировки?

Любая помощь будет отличной. Спасибо!

Ответы [ 3 ]

3 голосов
/ 23 июня 2011

Я придумал решение, которое предполагает последовательное попадание в каждый раздел. Обработка каждого раздела разбита на две части: часть «оболочка» и «содержимое». «Оболочка» отвечает за рендеринг <div class="FormatOne">...</div> битов, а «содержимое» отвечает за рендеринг фактического содержимого текущего раздела и всех последующих разделов, пока не будет найден несоответствующий раздел .

При обнаружении несоответствующего раздела управление возвращается к шаблону «оболочки» для этого раздела.

Это дает интересную гибкость: шаблоны «оболочки» могут быть очень агрессивными в том, что они соответствуют, а разделы «содержимого» могут быть более проницательными. В частности, в первом примере выходных данных вам нужно, чтобы элемент warning отображался как <span class="warningText">...</span>, и это достигается с помощью более точно соответствующего шаблона.

Все шаблоны «содержимого» после рендеринга содержимого их текущего раздела вызывают именованный шаблон, который ищет «следующий» соответствующий раздел содержимого. Это помогает консолидировать правила для определения того, что квалифицируется как «соответствующий» раздел.

Рабочий пример можно посмотреть здесь .

Вот мой код, созданный для воспроизведения того, что вы просили в первом примере:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" />

    <xsl:template match="/">
        <body>
            <xsl:apply-templates select="/root/section[1]" mode="shell" />
        </body>
    </xsl:template>

    <xsl:template match="section[
        @attr = 'someCriteria' or
        @attr = 'someOtherCriteria' or
        @attr = 'evenMoreCriteria' or
        @attr = 'criteriaSupreme']" mode="shell">

        <div class="FormatOne">
            <xsl:apply-templates select="." mode="contents" />
        </div>

        <xsl:apply-templates select="following-sibling::section[
            @attr != 'someCritera' and
            @attr != 'someOtherCriteria' and
            @attr != 'evenMoreCriteria' and
            @attr != 'criteriaSupreme'][1]" mode="shell" />

    </xsl:template>

    <xsl:template name="nextFormatOne">
        <xsl:variable name="next" select="following-sibling::section[1]" />
        <xsl:if test="$next[
            @attr = 'someCriteria' or
            @attr = 'someOtherCriteria' or
            @attr = 'evenMoreCriteria' or
            @attr = 'criteriaSupreme']">
            <xsl:apply-templates select="$next" mode="contents" />
        </xsl:if>
    </xsl:template>

    <xsl:template match="section[
        @attr = 'someCriteria' or
        @attr = 'someOtherCriteria']" mode="contents">

        <xsl:copy-of select="*" />

        <xsl:call-template name="nextFormatOne" />
    </xsl:template>

    <xsl:template match="section[@attr = 'evenMoreCriteria']" mode="contents">
        <span class="disclaimerText">
            <xsl:value-of select="disclaimer" />
        </span>

        <xsl:call-template name="nextFormatOne" />
    </xsl:template>

    <xsl:template match="section[@attr = 'criteriaSupreme']" mode="contents">
        <p class="copyright">
            <xsl:value-of select="p" />
        </p>

        <xsl:call-template name="nextFormatOne" />
    </xsl:template>

    <xsl:template match="section[@attr = 'anotherSetOfCriteria']" mode="shell">
        <div class="FormatTwo">
            <xsl:apply-templates select="." mode="contents" />
        </div>
        <xsl:apply-templates select="
            following-sibling::section[@attr != 'anotherSetOfCriteria'][1]"
            mode="shell" />
    </xsl:template>

    <xsl:template name="nextFormatTwo">
        <xsl:variable name="next" select="following-sibling::section[1]" />
        <xsl:if test="$next[@attr = 'anotherSetOfCriteria']">
            <xsl:apply-templates select="$next" mode="contents" />
        </xsl:if>
    </xsl:template>

    <xsl:template
        match="section[@attr = 'anotherSetOfCriteria']"
        mode="contents">

        <span class="warningText">
            <xsl:value-of select="warning" />
        </span>

        <xsl:call-template name="nextFormatTwo" />
    </xsl:template>

</xsl:stylesheet>
1 голос
/ 23 июня 2011

"Каждый раздел помещается в тот же сегмент, что и предыдущий раздел, если они имеют одинаковый формат. Если нет, создается новый сегмент."

То, что вы описали, по сути является задачей, выполняемойинструкция

<xsl:for-each-group group-adjacent="....">

в XSLT 2.0.Это предполагает, что вы можете написать функцию, которая преобразует ваши «критерии» в имя сегмента, и вызывать эту функцию в атрибуте, смежном с группой.

Итак, насколько сложно ограничение, которое вам нужно использовать?XSLT 1.0?

Если вы застряли с 1.0, то вам придется пойти по шаблону проектирования «брат-рекурсия», предложенному Крисом Нильсеном.

0 голосов
/ 23 июня 2011

Выезд template, if, choose и for-each:

Эти (как и другие XSLT-элементы) позволят вам выполнить условное поведение для переключения между различными логическими схемами преобразования.

...