Какое-то древнее дерьмо

Живёт тупо потому, что за хостинг уплачено на годы вперед

Разница между xsl:copy и xsl:copy-of

18 ноября 2010 · Комментариев: 2 · XSLT/XPath, Рецептарий

Постараюсь с минимумом воды раскрыть сабж. Итак, разница такова: xsl:copy-of осуществляет глубокое копирование (deep copy), а xsl:copy поверхностное (shallow copy).

Не особо непонятно? Заценим на примере.

Создадим перечень самых отстойных рок-альбомов 21 столетия:

<albums criteria="worst">
    <item id="1">
        <name>Chinese Democracy</name>
        <artist>Guns N' Roses</artist>
        <length>71:18</length>
        <label released="November 23, 2008">Geffen</label>
    </item>
    <item id="2">
        <name>Dark Horse</name>
        <artist>Nickelback</artist>
        <length>43:38</length>
        <label released="November 18, 2008">EMI</label>
    </item>
    <item id="3">
        <name>St. Anger</name>
        <artist>Metallica</artist>
        <length>75:01</length>
        <label released="June 5, 2003">Vertigo</label>
    </item>
</albums>

Теперь создадим xsl, который в точности копирует этот XML в результирующее дерево.

xsl:copy-of

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

Задача полностью решена. Мы создали точную копию albums со всеми атрибутами и детьми, осуществив таким образом буквальное копирование.

Посмотрим, что получится при попытке аналогичным образом использовать xsl:copy.

xsl:copy

Ну, для начала, у xsl:copy нет select. Зато внутри него можно использовать apply. Соответственно, если писать в лоб

<xsl:template match="/">
    <xsl:copy>
        <xsl:apply-templates select="*"/>
    </xsl:copy>
</xsl:template>

то в результате мы получим чуть более, чем ничего:

Chinese DemocracyGuns N' Roses71:18GeffenDark HorseNickelback43:38EMISt. AngerMetallica75:01Vertigo

Произошло это потому, что xsl:copy просто копирует текущую ноду, игнорируя атрибуты и разметку потомков. В итоге мы получили строковое значение корневого элемента.

Возникает вопрос, зачем нам тогда вообще xsl:copy? Нужен он для тех случаев, когда нам нужно скопировать дерево, внеся в него какие-то минимальные исправления, например, мы хотим исключить из финального результата информацию о длительности альбома.

xsl:copy-of создает буквальную копию, посему он нам не подходит. А вот с использованием xsl:copy эту задачу можно решить относительно изящно.
Но сначала нам нужно все-таки полностью прокинуть дерево, используя при этом таки поверхностное копирование. Для этого нам понадобится некий xpath и, ни много ни мало, рекурсия.

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

Про рекурсию, я думаю, понятно, а xpath этот делает следующее: “@*” выбирает все атрибуты, “node()” всех непосредственных потомков, независимо от их типа. Это если вкратце.
Таким образом мы не просто копируем дерево, а по сути обрабатываем его путем применения шаблона. А если мы применяем шаблон, то можем его переопределить, тем более шаблон match=”@* | node()” имеет низкий приоритет.

В итоге для удаления информации про длину альбома нам достаточно дописать пустой шаблон:

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

<xsl:template match="length"/>

Теперь усложняем задачу. Нам нужно в каждый альбом добавить фейспалму. Сделать это можно примерно так:

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

Итого, чтобы удалить length и добавить facepalm мы написали следующий код:

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

<xsl:template match="length"/>

получив нужный xml:

<albums criteria="worst">
    <item id="1">
        <name>Chinese Democracy</name>
        <artist>Guns N' Roses</artist>
        <label released="November 23, 2008">Geffen</label>
        <facepalm/>
    </item>
    <item id="2">
        <name>Dark Horse</name>
        <artist>Nickelback</artist>
        <label released="November 18, 2008">EMI</label>
        <facepalm/>
    </item>
    <item id="3">
        <name>St. Anger</name>
        <artist>Metallica</artist>
        <label released="June 5, 2003">Vertigo</label>
        <facepalm/>
    </item>
</albums>

Вывод

xsl:copy-of стоит использовать для буквального прокидывания дерева или его кусков в результат. xsl:copy хорош в тех случаях, когда нам необходимо внести точечные исправления.

Теги:

Комментариев: 2 ↓

  • MyFreeWeb

    Ахаха, отличный пример %)

    Chinese Democracy действительно отстойный? Я не слушал, я только знаю, что Декстер Холланд в шутку хотел назвать «Chinese DemocraZy» то, что назвал Splinter.
    «You snooze, you lose. Axl ripped off my braids, so I ripped off his album title» — Dexter

  • Flack

    Ну это просто не G’n’R. Если его так и не воспринимать, то, в общем, не настолько все плохо.

Оставить комментарий