Родительская / дочерняя группа Xslt

#xml #xslt #parent

#xml #xslt #родительская

Вопрос:

У меня проблема с получением следующего результата преобразования xml / xslt:

 <root>
  <node>   
    <id>1</id>
    <parentid></parentid>
    <name>file 1</name>
  <node>
  <node>   
    <id>2</id>
    <parentid></parentid>
    <name>file 2</name>
  <node>
  <node>   
    <id>3</id>
    <parentid>2</parentid>
    <name>file 3</name>
  <node>
  <node>   
    <id>4</id>
    <parentid></parentid>
    <name>file 4</name>
  <node>
  <node>   
    <id>5</id>
    <parentid>2</parentid>
    <name>file 5</name>
  <node>
</root>
  

я бы хотел, чтобы выходной html был чем-то вроде:

  <ul>
    <li>file 1</li>
    <li>file 2</li>
       <ul>
          <li>file 3</li>
          <li>file 5</li>
       </ul>
    <li>file 4</li>
 </ul>
  

Ответ №1:

 <?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" indent="yes"/>

    <xsl:strip-space elements="*"/>

    <xsl:template match="/root">
        <ul>
            <xsl:apply-templates select="node[string-length(parentid)=0]" />
        </ul>
    </xsl:template>

    <xsl:template match="node">
        <li>
            <xsl:value-of select="name"/>
        </li>
        <xsl:variable name="children" select="parent::*/node[parentid=current()/id]" />
        <xsl:if test="$children">
            <ul>
                <xsl:apply-templates select="$children" />
            </ul>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>
  

Комментарии:

1. Использование <xsl:key> имеет преимущество в производительности, которое, конечно, не имеет никакого значения при небольшом вводе. По крайней мере, для меня это также имеет преимущество в ясности, будучи более декларативным, менее процедурным — но это может быть вопросом личного вкуса.

2. Верно, ключ обладает лучшей производительностью для ввода большего объема, но для этого требуется понимание того, как работают ключи xsl, что не требуется для понимания базовой работы этого преобразования. Op, очевидно, новичок в XSLT, поэтому я постарался сделать его как можно более простым. Концептуально два решения практически идентичны, ни одно из них не является «процедурным» (использование for-each и call-template было бы процедурным).

3. @Alejandro, итак, как бы ты назвал стиль программирования при явном использовании конструкций с for-each и call-template ?

4. @Lucero: Ну, это зависит. Что касается стиля, существует классификация между стилями push и pull . Мне не нравятся шаблоны brick (корневое правило документа, выполняющее все преобразования), но в использовании инструкций xsl:for-each и xsl:call-template нет ничего концептуально неправильного.

5. @Alejandro, в Википедии есть следующее определение «декларативного программирования»: «Декларативное программирование часто определяется как любой стиль программирования, который не является императивным. […] Программа, которая описывает, какое вычисление должно быть выполнено, а не как его вычислить» — и это именно моя точка зрения. Хотя XSLT является декларативным языком, вы все равно можете использовать императивный стиль, используя конструкции, которые определяют что вместо как .

Ответ №2:

Ключ (без каламбура) заключается в использовании <xsl:key> для настройки отношений родитель-потомок. Как только это определено, остальное несложно.

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

    <xsl:key name="getchildren" match="node" use="parentid"/>

    <xsl:template match="root">
        <ul>
            <xsl:apply-templates
              select="node[parentid[not(normalize-space())]]"/>
        </ul>
    </xsl:template>

    <xsl:template match="node">
        <li><xsl:value-of select="name"/></li>
        <xsl:variable name="children" select="key( 'getchildren', id )"/>
        <xsl:if test="$children">
            <ul><xsl:apply-templates select="$children"/></ul>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>