как динамически вложить элементы с помощью XSLT? - PullRequest
0 голосов
/ 28 января 2019

Я пытаюсь сделать преобразование личности.Вот пример моего исходного XML:

<?xml version="1.0" encoding="UTF-8"?>
<text>
    <p id="542">This is a parapgraph</p>
    <p id="561">This is a first level bullet</p>
    <p id="561">This is a first level bullet</p>
    <p id="561">This is a first level bullet</p>
    <p id="561">This is a first level bullet</p>
    <p id="561">This is a first level bullet</p>
    <p id="542">This is a parapgraph</p>
    <p id="561">This is a first level bullet</p>
    <p id="562">This is a second level bullet</p>
    <p id="562">This is a second level bullet</p>
    <p id="561">This is a first level bullet</p>
    <p id="561">This is a first level bullet</p>
    <p id="542">This is a parapgraph</p>
    <p id="542">This is a parapgraph</p>
    <p id="560">This is a first ordered list</p>
    <p id="560">This is a first ordered list</p>
    <p id="560">This is a first ordered list</p>
    <p id="562">This is a second level bullet</p>
    <p id="562">This is a second level bullet</p>

</text>enter code here

Я ищу вывод, который выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<text>
    <p id="">This is a parapgraph</p>
    <ul>
    <li>This is a first level bullet</li>
    <li>This is a first level bullet</li>
    <li>This is a first level bullet</li>
    <li>This is a first level bullet</li>
    <li>This is a first level bullet</li>
    </ul>
    <p id="">This is a parapgraph</p>
    <ul>
    <li>This is a first level bullet
        <ul>
            <li>This is a second level bullet</li>
            <li>This is a second level bullet</li>
            <li>This is a second level bullet</li>
        </ul></li>
    <li>This is a first level bullet</li>
    <li>This is a first level bullet</li>
    </ul>
    <p id="">This is a parapgraph</p>
    <p id="">This is a parapgraph</p>
    <ol>
    <li>This is a first ordered list</li>
    <li>This is a first ordered list</li>
    <li>This is a first ordered list</li>
    <li>This is a first level bullet
        <ul>
            <li>This is a second level bullet</li>
            <li>This is a second level bullet</li>
        </ul>
    </li>
    <li>This is a first level bullet</li>
    </ol>
</text>

Не могли бы вы помочь мне понять, как это можно сделать?Я новичок в XSLT.Я искал учебник, который делает что-то подобное, но не смог найти.Мой текущий XSLT выглядит следующим образом:

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

    <xsl:output method="xml" standalone="no"/>

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


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

    <xsl:template match="topic">
        <book>
        <xsl:attribute name="id">
            <xsl:value-of select="generate-id(.)"/>
        </xsl:attribute>
        <xsl:apply-templates select="@*|node()"/>

        </book>
    </xsl:template>

    <xsl:template match="body">
        <text>

                <xsl:apply-templates select="p[@id='542']"/>
                <xsl:call-template name="f-ul"/>
                <xsl:call-template name="s-ul"/>
                <xsl:call-template name="o-l"/>

        </text>
    </xsl:template>

    <!-- all body text style to p tag> -->
    <xsl:template match="p[@id='542']">
        <p><xsl:apply-templates select="@*|node()" /></p>
    </xsl:template>

    <xsl:template name="f-ul">
    <xsl:if test="p[@id='561']">
        <ul>
            <xsl:attribute name="id">
                <xsl:text></xsl:text>
            </xsl:attribute>
            <xsl:apply-templates select="p[@id='561']"/>
        </ul>
    </xsl:if>
    </xsl:template>

    <xsl:template name="s-ul">
    <xsl:if test="p[@id='562']">
        <ul>
            <xsl:attribute name="id">
                <xsl:text></xsl:text>
            </xsl:attribute>
            <xsl:apply-templates select="p[@id='562']"/>
        </ul>
    </xsl:if>
    </xsl:template>

    <xsl:template name="o-l">
    <xsl:if test="p[@id='560']">
        <ol>
            <xsl:attribute name="id">
                <xsl:text></xsl:text>
            </xsl:attribute>
            <xsl:apply-templates select="p[@id='560']"/>
        </ol>
    </xsl:if>
    </xsl:template>



    <!-- all list number style to step tag> -->

    <xsl:template match="p[@id='560']">
        <li><xsl:apply-templates select="@*|node()" /></li>
    </xsl:template>


    <!-- all list bullet style to li tag> -->

    <xsl:template match="p[@id='561']">
        <li><xsl:apply-templates select="node()" /></li>
    </xsl:template>

    <!-- all list bullet 2 style to li tag> -->
    <xsl:template match="p[@id='562']">
        <li><xsl:apply-templates select="node()" /></li>
    </xsl:template>

</xsl:stylesheet>

Если вы можете помочь мне найти решение или, по крайней мере, направить меня к нужным ресурсам, это будет высоко оценено.Спасибо всем заранее.

1 Ответ

0 голосов
/ 28 января 2019

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

. Здесь представлен XSLT 3 (доступен с Saxon 9.8 или 9.9 для Java и .NET, а также в XML IDE, таких как oXygen или Stylus Studio, также реализованный Altova Raptor в ихлинейки продуктов с выпусков 2017 года) подход первого шага:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output method="xml" indent="yes"/>

  <xsl:param name="list-levels" as="map(xs:integer, xs:integer+)"
    select="map { 1 : (560, 561), 2: (562) }"/>

  <xsl:param name="list-map" as="map(xs:integer, xs:QName)"
    select="map { 560 : QName('', 'ol'), 561 : QName('', 'ul'), 562 : QName('', 'ul') }"/>

  <xsl:function name="mf:group" as="node()*">
      <xsl:param name="nodes" as="node()*"/>
      <xsl:param name="level" as="xs:integer"/>
      <xsl:for-each-group select="$nodes" group-adjacent="boolean(self::p[@id = $list-levels?($level to 6)])">
          <xsl:choose>
              <xsl:when test="current-grouping-key()">
                  <xsl:element name="{$list-map(xs:integer(@id))}">
                      <xsl:sequence select="mf:group(current-group(), $level + 1)"/>
                  </xsl:element>                  
              </xsl:when>
              <xsl:otherwise>
                  <xsl:apply-templates select="current-group()"/>
              </xsl:otherwise>
          </xsl:choose>
      </xsl:for-each-group>
  </xsl:function>

  <xsl:template match="text">
      <xsl:copy>
          <xsl:sequence select="mf:group(*, 1)"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="p[@id = 542]">
      <xsl:copy>
          <xsl:apply-templates/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="p[@id = $list-levels?*]">
      <li>
          <xsl:apply-templates/>
      </li>
  </xsl:template>

</xsl:stylesheet>

Вы можете установить его на https://xsltfiddle.liberty -development.net / pPzifoZ со следующим результатом:

<text>
   <p>This is a parapgraph</p>
   <ul>
      <li>This is a first level bullet</li>
      <li>This is a first level bullet</li>
      <li>This is a first level bullet</li>
      <li>This is a first level bullet</li>
      <li>This is a first level bullet</li>
   </ul>
   <p>This is a parapgraph</p>
   <ul>
      <li>This is a first level bullet</li>
      <ul>
         <li>This is a second level bullet</li>
         <li>This is a second level bullet</li>
      </ul>
      <li>This is a first level bullet</li>
      <li>This is a first level bullet</li>
   </ul>
   <p>This is a parapgraph</p>
   <p>This is a parapgraph</p>
   <ol>
      <li>This is a first ordered list</li>
      <li>This is a first ordered list</li>
      <li>This is a first ordered list</li>
      <ul>
         <li>This is a second level bullet</li>
         <li>This is a second level bullet</li>
      </ul>
   </ol>
</text>

Для второго шага я добавил режим обработки переноса вложенных ul или ol:

  <xsl:mode name="wrap" on-no-match="shallow-copy"/>

  <xsl:template match="ul | ol" mode="wrap">
      <xsl:copy>
          <xsl:for-each-group select="*" group-starting-with="li">
              <xsl:copy>
                  <xsl:apply-templates select="node(), current-group() except ." mode="#current"/>
              </xsl:copy>
          </xsl:for-each-group>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="text">
      <xsl:copy>
          <xsl:apply-templates select="mf:group(*, 1)" mode="wrap"/>
      </xsl:copy>
  </xsl:template>

Пример при https://xsltfiddle.liberty -развитии.net / pPzifoZ / 1 производит вывод

<text>
   <p>This is a parapgraph</p>
   <ul>
      <li>This is a first level bullet</li>
      <li>This is a first level bullet</li>
      <li>This is a first level bullet</li>
      <li>This is a first level bullet</li>
      <li>This is a first level bullet</li>
   </ul>
   <p>This is a parapgraph</p>
   <ul>
      <li>This is a first level bullet<ul>
            <li>This is a second level bullet</li>
            <li>This is a second level bullet</li>
         </ul>
      </li>
      <li>This is a first level bullet</li>
      <li>This is a first level bullet</li>
   </ul>
   <p>This is a parapgraph</p>
   <p>This is a parapgraph</p>
   <ol>
      <li>This is a first ordered list</li>
      <li>This is a first ordered list</li>
      <li>This is a first ordered list<ul>
            <li>This is a second level bullet</li>
            <li>This is a second level bullet</li>
         </ul>
      </li>
   </ol>
</text>

Полный код

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output method="xml" indent="yes"/>

  <xsl:param name="list-levels" as="map(xs:integer, xs:integer+)"
    select="map { 1 : (560, 561), 2: (562) }"/>

  <xsl:param name="list-map" as="map(xs:integer, xs:QName)"
    select="map { 560 : QName('', 'ol'), 561 : QName('', 'ul'), 562 : QName('', 'ul') }"/>

  <xsl:function name="mf:group" as="node()*">
      <xsl:param name="nodes" as="node()*"/>
      <xsl:param name="level" as="xs:integer"/>
      <xsl:for-each-group select="$nodes" group-adjacent="boolean(self::p[@id = $list-levels?($level to 6)])">
          <xsl:choose>
              <xsl:when test="current-grouping-key()">
                  <xsl:element name="{$list-map(xs:integer(@id))}">
                      <xsl:sequence select="mf:group(current-group(), $level + 1)"/>
                  </xsl:element>                  
              </xsl:when>
              <xsl:otherwise>
                  <xsl:apply-templates select="current-group()"/>
              </xsl:otherwise>
          </xsl:choose>
      </xsl:for-each-group>
  </xsl:function>

  <xsl:mode name="wrap" on-no-match="shallow-copy"/>

  <xsl:template match="ul | ol" mode="wrap">
      <xsl:copy>
          <xsl:for-each-group select="*" group-starting-with="li">
              <xsl:copy>
                  <xsl:apply-templates select="node(), current-group() except ." mode="#current"/>
              </xsl:copy>
          </xsl:for-each-group>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="text">
      <xsl:copy>
          <xsl:apply-templates select="mf:group(*, 1)" mode="wrap"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="p[@id = 542]">
      <xsl:copy>
          <xsl:apply-templates/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="p[@id = $list-levels?*]">
      <li>
          <xsl:apply-templates/>
      </li>
  </xsl:template>

</xsl:stylesheet>

Примеры используют XSLT 3, но в XSLT 2 группировка будет такой же, сохраняя толькоотображение p идентификаторов науровни элементов и списков должны были бы быть выполнены в некоторой структуре XML вместо XPath map s, который я использовал выше.

Ограничение: в $level to 6 я выбрал некоторый максимальный уровень вложенности, равный шести, но он можетконечно легко подстраиваться.

Тем не менее, выбор более умного представления, такого как массив, позволит нам избежать необходимости определять максимальный уровень, поэтому с <xsl:param name="list-levels" as="array(xs:integer+)" select="[ (560, 561), (562) ]"/> и затем <xsl:for-each-group select="$nodes" group-adjacent="boolean(self::p[@id = array:subarray($list-levels, $level)])">, как в https://xsltfiddle.liberty -development.net/ pPzifoZ / 2 , нет необходимости определять максимальный уровень, жестко закодированный в XSLT.

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