Постараюсь с минимумом воды раскрыть сабж. Итак, разница такова: 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 хорош в тех случаях, когда нам необходимо внести точечные исправления.
MyFreeWeb // 18 ноября 2010 в 01:57
Ахаха, отличный пример %)
Chinese Democracy действительно отстойный? Я не слушал, я только знаю, что Декстер Холланд в шутку хотел назвать «Chinese DemocraZy» то, что назвал Splinter.
«You snooze, you lose. Axl ripped off my braids, so I ripped off his album title» — Dexter
Flack // 20 ноября 2010 в 18:36
Ну это просто не G’n'R. Если его так и не воспринимать, то, в общем, не настолько все плохо.
sepulchered // 24 июня 2011 в 12:26
Да, норм альбом хороший. Не стоил тех 10 лет, которые на него потратили, но всё равно норм альбом. Ст.Ангер тоже не такой отстой…хотя реально отстой, а никльбэк сами по себе аццтой (вообщем дело вкуса:) ).