создать HTML из списка узлов XML, используя XSLT - PullRequest
2 голосов
/ 16 марта 2011

Я нуб на XSLT. У меня есть XML, где за t-узлами следуют другие узлы, а затем снова может появиться другой t-узел, за которым снова следуют узлы и т. Д.

<t />
<n1 />
<n2 />
..

<t/>
<n3 />
<n4 />
...

Что мне нужно, чтобы превратить этот XML в HTML, где t-узлы переносят все узлы, следующие за ним, до следующего t-узла

<div class='t'>
   <div class='n1'/>
   <div class='n2'/>
    ...
</div>

<div class='t'>
   <div class='n3'/>
   <div class='n4'/>
    ...
</div>

Мне трудно это реализовать. Есть идеи \ советы?

Спасибо!

Ответы [ 2 ]

6 голосов
/ 16 марта 2011

Это группировка смежных областей. Есть много решений:

С этим хорошо сформированным входом:

<root>
    <t />
    <n1 />
    <n2 />
    <t/>
    <n3 />
    <n4 />
</root>

XSLT 1.0: перемещение по следующей оси

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:template match="root">
        <xsl:copy>
            <xsl:apply-templates select="node()[1]" mode="group"/>
            <xsl:apply-templates select="t"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="t">
        <div class="t">
            <xsl:apply-templates select="following-sibling::node()[1]"
                                 mode="group"/>
        </div>
    </xsl:template>
    <xsl:template match="t" mode="group"/>
    <xsl:template match="node()" mode="group">
        <xsl:apply-templates select="."/>
        <xsl:apply-templates select="following-sibling::node()[1]"
                             mode="group"/>
    </xsl:template>
    <xsl:template match="*[starts-with(name(),'n')]">
        <div class="{name()}"/>
    </xsl:template>
</xsl:stylesheet>

XSLT 1.0: ключи

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:key name="kNodeByMark"
             match="node()[../t][not(self::t)]"
             use="generate-id((..|preceding-sibling::t[1])[last()])"/>
    <xsl:template match="root">
        <xsl:copy>
            <xsl:apply-templates select="key('kNodeByMark',generate-id())"/>
            <xsl:for-each select="t">
                <div class="t">
                    <xsl:apply-templates
                         select="key('kNodeByMark',generate-id())"/>
                </div>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[starts-with(name(),'n')]">
        <div class="{name()}"/>
    </xsl:template>
</xsl:stylesheet>

XSLT 2.0: for-each-group инструкция

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:template match="root">
        <xsl:copy>
            <xsl:apply-templates select="node()[../t[1] >> .]"/>
            <xsl:for-each-group select="node()" group-starting-with="t">
                <div class="t">
                    <xsl:apply-templates 
                         select="current-group()[position()>1]"/>
                </div>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[starts-with(name(),'n')]">
        <div class="{name()}"/>
    </xsl:template>
</xsl:stylesheet>

Выход:

<root>
    <div class="t">
        <div class="n1" />
        <div class="n2" />
    </div>
    <div class="t">
        <div class="n3" />
        <div class="n4" />
    </div>
</root>

РЕДАКТИРОВАТЬ : перемещение по следующей оси изменено, чтобы выглядеть как другие решения Отмена правил идентификации.

0 голосов
/ 16 марта 2011

См. Мою заметку по вашему вопросу относительно "какой версии XSLT?".Если в вашей целевой версии поддерживается группировка, см. Другие ответы здесь, поскольку это легче понять и почти наверняка будет работать лучше на любом процессоре XSLT.Если вы не уверены, я рекомендую использовать решение 1.0, подобное этому.

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

В этом решении ниже, я попытался сохранить прямую корреляцию между формой XSLT и формой вывода, который вы хотите.По моему мнению, это облегчает поддержку / понимание, по крайней мере, для небольших таблиц стилей.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/root">
    <xsl:for-each select="t">
      <div class='t'>
        <xsl:for-each select="following-sibling::*[count(preceding-sibling::t)=(count(current()/preceding-sibling::t) + 1) and not(self::t)]">
          <div class='{name()}' />
        </xsl:for-each>
      </div>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Правая часть "follow-sibling :: * [count (previous-sibling :: t)"= (count (current () / previous-sibling :: t) + 1) и not (self :: t)] "можно упростить, я уверен, используя что-то вроде" current () :: position () "(что неверно, к вашему сведению), но я ржавый и не могу вспомнить некоторые из синтаксиса псевдонимов.

Это в основном говорит: 1) Оценивать каждый T. 2) Выберите элементы с одинаковым количествомиз T, предшествующего им, в качестве индекса T, который мы в настоящее время оцениваем.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...