Если порядок свойств в 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.