xslt 2.0 xsl: число рекурсивного счетчика - более эффективный способ? - PullRequest
0 голосов
/ 29 июня 2018

Я хочу заполнить все элементы атрибута / значения, чье имя - «порядок», возрастающим значением счетчика.

...
<attribute>
    <name>order</name>
    <value></value>
</attribute>
...

Атрибуты «порядка» могут появляться в любом месте документа и на любой глубине. Значение заказа не обязательно должно быть точно точным, но каждый атрибут заказа ниже в документе должен иметь более высокое значение, чем все предыдущие атрибуты заказа.

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

<xsl:template match="node() | @*" mode="postprocess">   
    <xsl:copy>
        <xsl:apply-templates select="node() | @*" mode="postprocess" />
    </xsl:copy>
</xsl:template>
<xsl:template match="*[self::n:value and parent::n:attribute[n:name = 'order']]" mode="postprocess">        
    <value>
        <xsl:number level="any" count="*"/>
    </value>
</xsl:template> 

Это работает, но не кажется очень производительным. Я работаю с относительно большими файлами - около 20 МБ - и приведенный выше код подсчета увеличивает время обработки примерно на 50%.

Я нашел похожий вопрос здесь: высокопроизводительная альтернатива xsl: number но нет хороших ответов для меня.

Есть ли лучший способ?

Я использую Saxon HE 9.7.0.

Ответы [ 3 ]

0 голосов
/ 29 июня 2018

Вы можете использовать этот код, может он вам поможет:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xpath-default-namespace="n"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:output method="xml" indent="yes" />

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

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

    <xsl:template match="value[../name = 'order']">
        <xsl:copy>
            <xsl:value-of select="count(preceding::value[../name = 'order'])+1"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>
0 голосов
/ 30 июня 2018

Вы можете переписать или изменить как шаблон соответствия шаблона, так и шаблон подсчета, чтобы попытаться улучшить производительность:

<xsl:template match="n:attribute[n:name = 'order']/n:value" mode="postprocess">        
    <value>
        <xsl:number level="any" count="n:attribute[n:name = 'order']/n:value"/>
    </value>
</xsl:template> 

Ваш комментарий предполагает, что эти изменения улучшили производительность для вас, поэтому я публикую это как ответ.

0 голосов
/ 29 июня 2018

Вот альтернативный способ, хотя я понятия не имею, улучшает ли он производительность или нет.

Сначала создайте переменную, которую можно эффективно использовать для сопоставления элементов value с их положением

<xsl:variable name="attributes">
    <xsl:for-each select="//n:attribute[n:name = 'order']/n:value">
        <n:value key="{generate-id()}" pos="{position()}" />
    </xsl:for-each>
</xsl:variable>

Затем создайте ключ для поиска этих значений

<xsl:key name="attributes" match="n:value" use="@key" />

Затем, вместо использования xsl:number, получите позицию следующим образом:

<xsl:value-of select="key('attributes', generate-id(), $attributes)/@pos" />

Например, с учетом этого XML

<root xmlns="n">
<attribute>
    <name>order</name>
    <value></value>
</attribute>
<attribute>
    <name>order</name>
    <value></value>
</attribute>
<children>
    <attribute>
        <name>order</name>
        <value></value>
    </attribute>
</children>
</root>

А это XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:n="n" exclude-result-prefixes="n">

<xsl:key name="attributes" match="n:value" use="@key" />

<xsl:variable name="attributes">
    <xsl:for-each select="//n:attribute[n:name = 'order']/n:value">
        <n:value key="{generate-id()}" pos="{position()}" />
    </xsl:for-each>
</xsl:variable>

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

<xsl:template match="n:attribute[n:name = 'order']/n:value">
    <value xmlns="n">
        <xsl:value-of select="key('attributes', generate-id(), $attributes)/@pos" />
    </value>
</xsl:template> 
</xsl:stylesheet>

Результат таков:

<attribute>
    <name>order</name>
    <value>1</value>
</attribute>
<attribute>
    <name>order</name>
    <value>2</value>
</attribute>
<children>
    <attribute>
        <name>order</name>
        <value>3</value>
    </attribute>
</children>
</root>
...