Применение временных деревьев № 4 — подстройка под готовый шаблон

HTML и CSSXSLTJavaScriptИзображенияСофтEtc
Александр Самиляк

5 июля 2011


Задача.

Разобрать по косточкам теорию и практику использования временных деревьев в XSLT.

Несмотря на все природное многообразие сайтов, в них нередко встречаются типовые блоки, которые повторно используются на многих страницах. Например, блок файла для скачивания:

В XML этот файл может выглядеть так:

 <file
  src="/r/report.doc"
  name="report.doc"
  ext="DOC"
  size="43 KБ"
>
  Квартальный отчет
</file>

А XSL, который превращает этот файл в HTML, — так:

 <xsl:template match="file[@src and (string(.) or @name)]" mode="html">
  <span class="file {@ext}">
    <a href="{@src}" target="_blank">
      <i /> <!-- Без иконочки никак нельзя -->
  
      <xsl:choose>
        <xsl:when test="string(.)">
          <xsl:value-of select="." />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="@name" />
        </xsl:otherwise>
      </xsl:choose>
    </a>
    <nobr>
      <xsl:value-of select="concat(@ext, ', ', @size)"/>
    </nobr>
  </span>
</xsl:template>

И на выходе получаем нужный нам HTML:

 <span class="file DOC">
  <a href="/r/report.doc" target="_blank">
    <i/>
    Квартальный отчет
  </a>
  <nobr>DOC, 43 KБ</nobr>
</span>

Все отлично работает, пока во входящем XML файл для скачивания представлен именно в таком виде — имя элемента <file>, путь находится в атрибуте src и т. д.

Но ничто не вечно под луной, и того серверного программиста, который еще вчера выдавал нам указанный XML, сегодня уволили. Вместо уволенного пришел новый, и теперь в нашем XML завелся второй формат файла для скачивания:

 <File
  fullname="/r/report.doc"
  name="report.doc"
  extension="DOC"
  pretty_size="43 KБ"
>
  <label>Квартальный отчет</label>
</File>

Имя тега другое (не забываем, что XML регистрозависим), путь до файла — в новом атрибуте fullname, и вообще все по-другому. Серверный программер попался угрюмый, и менять XML отказывается. Да и, по правде сказать, несерьезно это — заставлять новый XML подстраиваться под старый XSL.

По всему сайту полно файлов в старом формате, поэтому править исходный шаблон мы не можем. Делаем новый:

 <xsl:template match="File[@fullname and (label or @name)]" mode="html">
  <span class="file {@extension}">
    <a href="{@fullname}" target="_blank">
      <i />
  
      <xsl:choose>
        <xsl:when test="label">
          <xsl:value-of select="label" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="@name" />
        </xsl:otherwise>
      </xsl:choose>
    </a>
    <nobr>
      <xsl:value-of select="concat(@extension, ', ', @pretty_size)"/>
    </nobr>
  </span>
</xsl:template>

Мы написали второй шаблон, отвечающий новому формату XML. Чем этот шаблон отличается от первого? Да ничем. Ведь мы вывели тот же HTML, ибо для него уже написан CSS, только бери да пользуйся. Налицо дублирование кода.

А проблема ведь заключается лишь в одном: у нас не тот формат входящего XML. Так сделаем его тем, с помощью временного дерева:

 <xsl:template match="File[@fullname and (label or @name)]" mode="html">
  <xsl:variable name="file">
    <file
      src="{@fullname}"
      name="{@name}"
      ext="{@extension}"
      size="{@pretty_size}"
    >
      <xsl:copy-of select="label/node()" />
    </file>
  </xsl:variable>
  
  <xsl:apply-templates select="exsl:node-set($file)/file" mode="html" />
</xsl:template>

При таком раскладе первый шаблон match="file[@src and (string(.) or @name)]" ни о чем не догадается и с легкостью выдаст нам желанную разметку файла для скачивания. К слову сказать, это главная задача временных деревьев — собирать такие удобные XML-структуры, которых нет во входящем дереве (или есть, но в плохом формате).


Как обычно, это не единственное решение задачи. Мы могли бы выделить именованный шаблон name="html_file" с кучей параметров: src, name, ext, size, label (целых пять штук). И затем как в первом шаблоне match="file[…]", так и во втором match="File[…]" можно было бы вызывать этот именованный шаблон name="html_file", передавая ему нужные параметры. Формат входящего XML в таком случае может быть любым, поскольку каждый атрибут файла для скачивания передается отдельным параметром.

Здесь также прослеживается связь с применением № 2 — передачей сложных параметров. Вместо того чтобы делать 5 простых параметров, мы сделали один сложный, в который положили все атрибуты файла для скачивания. Разница лишь одна: здесь временное дерево передается не в параметре именованного шаблона, как это было раньше, а отправляется на обработку в match другого шаблона.


У нас осталось еще одно применение временных деревьев — многоступенчатая трансформация. Об этом — в следующей части.

1
2
3
4
5
6
7
8
9