Несколько неочевидных моментов по XSLT.
1. avg, min, max
В XSLT нет агрегатных функций avg()
, min()
, max()
. Есть только count()
и sum()
.
Предположим, у нас есть такой XML:
<list>
<item>10</item>
<item>5</item>
<item>25</item>
</list>
Среднее значение можно посчитать так:
<xsl:variable name="avg" select="sum(/list/item) div count(/list/item)"/>
Минимум и максимум чуть сложнее. Бежим по отсортированным по возрастанию/убыванию нодам и берем первую:
<xsl:variable name="min">
<xsl:for-each select="/list/item">
<xsl:sort data-type="number" order="ascending"/>
<xsl:if test="position() = 1"><xsl:value-of select="."/></xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="max">
<xsl:for-each select="/list/item">
<xsl:sort data-type="number" order="descending"/>
<xsl:if test="position() = 1"><xsl:value-of select="."/></xsl:if>
</xsl:for-each>
</xsl:variable>
2. current()
current()
внутри квадратных скобок позволяет выйти из контекста ноды к которой применены эти скобки и вернуться к контексту текущей ноды (которая была заматчена шаблоном или заселекчена <xsl:for-each>
).
То есть вместо того, чтобы создавать переменную:
<xsl:template match="navigation">
<xsl:variable name="status" select="@status"/>
<xsl:value-of select="/captions/caption[@status = $status]"/>
</xsl:template>
можно написать так:
<xsl:template match="navigation">
<xsl:value-of select="/captions/caption[@status = current()/@status]"/>
</xsl:template>
3. concat()
Работает быстрее:
<xsl:value-of select="concat(@Name, ' ', @Surname)"/>
чем:
<xsl:value-of select="@Name"/>
<xsl:value-of select="' '"/>
<xsl:value-of select="@Surname"/>
Потому что в первом случае происходит только одна операция вставки в результирующее XML-дерево.
Не говоря уже о том, что первый вариант компактнее.
4. not()
vs. !=
В некоторых версиях XSLT-процессоров есть разница:
not(@name = 'Stepan')
<a name="Stepan"/> => false
<a name="Leechy"/> => true
<a/> => true
@name != 'Stepan'
<a name="Stepan"/> => false
<a name="Leechy"/> => true
<a/> => false
Пару раз когда использовал !=
сталкивался с тем, что вылезала бага при переносе кода с разработческого сервера на хостинг, где стоял другой XSLT-процессор. Поэтому рекомендую всегда использовать not()
.
5. Глобальные переменные → ускорение
Переменная Regions
вычисляется каждый раз при вызове шаблона для <MuscatRow>
:
<xsl:template match="MuscatRow">
<xsl:variable name="Regions" select="/Imprimatur/Element[@Name = 'Regions']/Document"/>
<xsl:value-of select="$Regions[@Name = current()/@Name]/content"/>
</xsl:template>
Используя глобальную переменную можно добиться некоторого ускорения. Переменная Regions
вычисляется один раз:
<xsl:variable name="Regions" select="/Imprimatur/Element[@Name = 'Regions']/Document"/>
<xsl:template match="MuscatRow">
<xsl:value-of select="$Regions[@Name = current()/@Name]/content"/>
</xsl:template>
Актуально при большом количестве <MuscatRow>
.
6. Отсчет preceding-sibling-ов
preceding-sibling[1] — это ближайший предыдущий элемент.
Получается вот что:
preceding-sibling[last()]
...
preceding-sibling[2]
preceding-sibling[1]
current item
following-sibling[1]
following-sibling[2]
...
following-sibling[last()]