Пакетный режим против полностью потокового XSLT - PullRequest
1 голос
/ 07 мая 2019

Я написал XSLT для преобразования огромного входящего XML-файла в JSON с использованием потоковой передачи в пакетном режиме.Я новичок в XSLT и слышал, что есть лучший способ полной потоковой передачи кода XSLT, который более эффективен и быстрее, чем пакетный режим.

Может кто-нибудь помочь мне понять - 1. В чем разница между режимом серийной съемки и потоковой передачей?2. Как я могу преобразовать приведенный ниже XSLT-код в полностью потоковую, чтобы улучшить производительность?

Ниже приведен мой XSLT-код в пакетном режиме -

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wd="urn:com.workday.report/INT1109_CR_REV_Customer_Invoices_to_Connect" exclude-result-prefixes="xs" version="3.0">
    <xsl:mode streamable="yes" on-no-match="shallow-skip"/>
    <xsl:output method="text" encoding="UTF-8" indent="no"/>
    <xsl:template match="wd:Report_Data">
        <xsl:iterate select="wd:Report_Entry/copy-of()">
            <!--Define Running Totals for Statistics -->
            <xsl:param name="TotalHeaderCount" select="0"/>
            <xsl:param name="TotalLinesCount" select="0"/>
            <!--Write Statistics -->
            <xsl:on-completion>
                <xsl:text>{"Stats": </xsl:text>
                <xsl:text>{"Total Header Count": </xsl:text>
                <xsl:value-of select="$TotalHeaderCount"/>
                <xsl:text>,</xsl:text>
                <xsl:text>"Total Lines Count": </xsl:text>
                <xsl:value-of select="$TotalLinesCount"/>
                <xsl:text>}}</xsl:text>
            </xsl:on-completion>
            <!--Write Header Details -->
            <xsl:text>{"id": "</xsl:text>
            <xsl:value-of select="wd:id"/>
            <xsl:text>",</xsl:text>
            <xsl:text>"revenue_stream": "</xsl:text>
            <xsl:value-of select="wd:revenue_stream"/>
            <xsl:text>",</xsl:text>
            <!--Write Line Details -->
            <xsl:text>"lines": [  </xsl:text>
            <!-- Count the number of lines for an invoice -->
            <xsl:variable name="Linescount" select="wd:total_lines"/>
            <xsl:iterate select="wd:lines">
                <xsl:text>      {</xsl:text>
                <xsl:text>"sequence": </xsl:text>
                <xsl:value-of select="wd:sequence"/>
                <xsl:text>,</xsl:text>
                <xsl:text>"sales_item_id": "</xsl:text>
                <xsl:value-of select="wd:sales_item_id"/>
                <xsl:text>",</xsl:text>
            </xsl:iterate>
            <xsl:text>}]}&#10;</xsl:text>
            <!--Store Running Totals -->
            <xsl:next-iteration>
                <xsl:with-param name="TotalHeaderCount" select="$TotalHeaderCount + 1"/>
                <xsl:with-param name="TotalLinesCount" select="$TotalLinesCount + $Linescount"/>                
            </xsl:next-iteration>
        </xsl:iterate>
    </xsl:template>
</xsl:stylesheet>

Вот пример XML -

<?xml version="1.0" encoding="UTF-8"?>
<wd:Report_Data xmlns:wd="urn:com.workday.report/INT1109_CR_REV_Customer_Invoices_to_Connect">
    <wd:Report_Entry>
        <wd:id>CUSTOMER_INVOICE-6-1</wd:id>
        <wd:revenue_stream>TESTA</wd:revenue_stream>        
        <wd:total_lines>1</wd:total_lines>
        <wd:lines>
            <wd:sequence>ab</wd:sequence>
            <wd:sales_item_id>Administrative Cost</wd:sales_item_id>            
        </wd:lines>
    </wd:Report_Entry>
    <wd:Report_Entry>
        <wd:id>CUSTOMER_INVOICE-6-10</wd:id>
        <wd:revenue_stream>TESTB</wd:revenue_stream>        
        <wd:total_lines>1</wd:total_lines>
        <wd:lines>
            <wd:sequence>ab</wd:sequence>
            <wd:sales_item_id>Data - Web Access</wd:sales_item_id>
        </wd:lines>
    </wd:Report_Entry>  
</wd:Report_Data>

1 Ответ

1 голос
/ 08 мая 2019

Если порядок свойств в JSON не имеет значения, вы можете напрямую создавать карты и массивы XSLT / XPath 3 с xsl:map/xsl:map-entry (или конструктором XPath 3.1 map) и специфическим для Saxon элементом расширения saxon:array(к сожалению, в стандарте языка XSLT 3 отсутствует инструкция по созданию массива).Кроме того, большинство ваших параметров итерации, кажется, легко реализуются как аккумуляторы:

<?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"
    xmlns:saxon="http://saxon.sf.net/"
    extension-element-prefixes="saxon"
    xpath-default-namespace="urn:com.workday.report/INT1109_CR_REV_Customer_Invoices_to_Connect"
    exclude-result-prefixes="#all"
    version="3.0">

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

    <xsl:mode streamable="yes" use-accumulators="#all" on-no-match="shallow-skip"/>

    <xsl:accumulator name="header-count" as="xs:integer" initial-value="0" streamable="yes">
        <xsl:accumulator-rule match="Report_Entry" select="$value + 1"/>
    </xsl:accumulator>

    <xsl:accumulator name="lines-count" as="xs:integer" initial-value="0" streamable="yes">
        <xsl:accumulator-rule match="Report_Entry/total_lines/text()" select="$value + xs:integer(.)"/>
    </xsl:accumulator>

    <xsl:template match="Report_Data">
        <xsl:apply-templates/>
        <xsl:sequence
            select="map {
                     'Stats': map { 
                          'Total Header Count' : accumulator-after('header-count'),
                          'Total Lines Count' : accumulator-after('lines-count')
                        }
                    }"/>
    </xsl:template>

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

    <xsl:template match="Report_Entry/id | Report_Entry/revenue_stream | lines/sequence | lines/sales_item_id">
        <xsl:map-entry key="local-name()" select="string()"/>
    </xsl:template>

    <xsl:template match="Report_Entry/lines">
        <xsl:map-entry key="local-name()">
            <saxon:array>
                <xsl:apply-templates/>
            </saxon:array>
        </xsl:map-entry>
    </xsl:template>

</xsl:stylesheet>

В примере используется адаптивный метод вывода, поскольку в вашем текущем примере не создается ни один объект JSON, и я просто попытался создать тот же самыйвыводить как ваш текущий код;методу вывода JSON потребуется одна карта или массив в качестве результата основной последовательности.

Код работает с потоковой передачей и Saxon EE 9.9.1.1 в oXygen, к сожалению, 9.8 не считает код пригодным для обработки.

Что касается общих правил, существуют ограничения в отношении того, чего можно достичь с помощью аккумуляторов и сопоставления шаблонов при потоковой передаче;Как вы можете видеть, аккумулятор для суммирования значений из элементов total_lines должен соответствовать дочернему тексту, чтобы не использовать элемент в аккумуляторе (однако Saxon имеет другое расширение для сбора аккумуляторов, чтобы облегчить такие задачи).

Пока что я бы скорее сказал, что более важно найти способ обойти анализ стримабельности и сделать так, чтобы стримабельный код возвращал правильный и тот же результат, что и нестраиваемый код;например, при экспериментировании с подходом к генерации JSON с потоковой передачей с использованием двух шагов преобразования, где вводятся некоторые примеры данных, аналогичные вашим, представление XML для JSON - результат первого преобразования, а JSON - как результат использования xml-to-json в результате первого шага я столкнулся с ошибкой Саксонии https://saxonica.plan.io/issues/4215.

При потоковой передаче, кажется, недостаточно покрытия тестов или зрелости реализации, чтобы иметь возможность надежно комбинировать функции в сложном имасштабируемым образом, частично из-за сложной спецификации, частично из-за ограниченного использования этого материала сообществом XSLT.

Таким образом, если вы найдете способ решения конкретной проблемы использовать потоковую передачу, чтобы сохранить потребление памяти более низким или управляемым по сравнению с обычной обработкой на основе дерева XSLT 2/3, то вы, конечно, можете поэкспериментировать с изменениями, чтобы повысить производительность, ноэто легко сломать вещи.

Одним из общих замечаний является то, что потоковая передача позволяет получить доступ ко всем атрибутам текущего обработанного / сопоставленного элемента, но не к его дочерним элементам, поэтому он может очень помочь вставить этапы обработки, которые преобразуют элементы в атрибуты, если у вас есть простойструктура дочернего элемента.Таким образом, вы можете часто избегать любых copy-of().Но, конечно, вам нужен способ объединения двух таблиц стилей, которые Saxon позволяет с его API, но для этого требуется написание кода Java или .NET.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...