У меня есть очень большой входной документ (тысячи записей), который имеет структуру что-то вроде этого (Data представляет множество дочерних элементов):Использование XSL: аккумулятор с XSL: попробуйте/XSL: поймать
<Input>
<Record id="1">
<Data/>
</Record>
<Record id="2">
<Data/>
</Record>
<Record id="3">
<Data/>
</Record>
<Record id="4">
<Data/>
</Record>
<Record id="5">
<Data/>
</Record>
<Record id="6">
<!-- This is bad data -->
<BadData/>
</Record>
<Record id="7">
<Data/>
</Record>
<Record id="8">
<Data/>
</Record>
<Record id="9">
<!-- Also bad data -->
<BadData/>
</Record>
</Input>
Я m обрабатывать его таблицей стилей, которая выполняет сложное преобразование на каждой записи, которая может работать во многих динамических ошибках. В этом приложении, если в нескольких записях есть плохие данные, я бы предпочел не останавливать преобразование, но я хотел бы узнать об ошибках, чтобы потом исправить их. Я использую XSL: попробуйте/XSL: улов, чтобы позволить обработку для продолжения:
<xsl:stylesheet
version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:err="http://www.w3.org/2005/xqt-errors"
exclude-result-prefixes="xs err">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="Input">
<Output>
<xsl:apply-templates/>
</Output>
</xsl:template>
<xsl:template match="Record">
<xsl:variable name="preprocessed" as="element(GoodData)?">
<xsl:try>
<xsl:apply-templates mode="preprocess" select="."/>
<xsl:catch>
<xsl:message expand-text="yes">Couldn't create good data for {@id} Code: {$err:code} {$err:description}</xsl:message>
</xsl:catch>
</xsl:try>
</xsl:variable>
<!-- Do some more logic on the preprocessed record -->
<xsl:if test="$preprocessed">
<NewRecord id="{@id}">
<xsl:sequence select="$preprocessed"/>
</NewRecord>
</xsl:if>
</xsl:template>
<xsl:template mode="preprocess" match="Record">
<!-- This represents a very complex transform with many potential dynamic errors -->
<xsl:variable name="source" as="element(Data)" select="*"/>
<xsl:if test="$source">
<GoodData/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Это прекрасно работает, но это боль, чтобы вырыть через большие входные документы, чтобы найти несколько записей, которые не удалось. Я бы хотел написать источник элементов Record, которые не могут получить новый входной документ с использованием xsl: result-document. Я пытаюсь добавить XSL: аккумуляторный что-то вроде этого:
<xsl:accumulator name="failed-source" initial-value="()" as="element(Record)*">
<xsl:accumulator-rule match="Record" phase="end">
<xsl:sequence select="$value, .[false()(:test for failure:)]"/>
</xsl:accumulator-rule>
</xsl:accumulator>
<xsl:template match="Input">
<Output>
<xsl:apply-templates/>
</Output>
<xsl:if test="accumulator-after('failed-source')">
<xsl:result-document href="failed.input.xml">
<Input>
<xsl:sequence select="accumulator-after('failed-source')"/>
</Input>
</xsl:result-document>
</xsl:if>
</xsl:template>
Однако, я не могу понять, что предикат в XSL: Накопитель-правило должно быть, или, если это вообще возможно использовать этот шаблон. Можно создать единый результирующий документ без изменения таблицы стилей?
NB: Я знаю следующее решение, но это был не мой первый выбор, потому что похоже, что он потенциально может иметь гораздо более высокие требования к памяти, но, возможно, это неверно. Я мог бы также записать все записи в отдельные файлы, но считаю это опасным, потому что один исходный документ может генерировать тысячи сбоев.
<xsl:template match="Input">
<xsl:variable name="processed" as="document-node()">
<xsl:document>
<xsl:apply-templates/>
</xsl:document>
</xsl:variable>
<xsl:if test="$processed/NewRecord">
<Output>
<xsl:sequence select="$processed/NewRecord"/>
</Output>
</xsl:if>
<xsl:if test="$processed/Record">
<xsl:result-document href="failed.input.xml">
<Input>
<xsl:sequence select="$processed/Record"/>
</Input>
</xsl:result-document>
</xsl:if>
</xsl:template>
<xsl:template match="Record">
<xsl:variable name="preprocessed" as="element(GoodData)?">
<xsl:try>
<xsl:apply-templates mode="preprocess" select="."/>
<xsl:catch>
<xsl:message expand-text="yes">Couldn't create good data for {@id} Code: {$err:code} {$err:description}</xsl:message>
</xsl:catch>
</xsl:try>
</xsl:variable>
<!-- Do some more logic on the preprocessed record -->
<xsl:choose>
<xsl:when test="$preprocessed">
<NewRecord id="{@id}">
<xsl:sequence select="$preprocessed"/>
</NewRecord>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Я подумал о написании функции проверки, но это будет дублировать много работы. Исходная схема не удобна для пользователя, поэтому гораздо удобнее ловить ошибки во время обработки. Например. «Пустая последовательность не допускается, так как значение переменной $ year намного легче справляется, чем что-то вроде« существует (A21/@ att1) ». Правильно ли я полагаю, что последний подход, описанный выше, будет иметь более высокий профиль памяти, чем какой-то аккумулятор? – nine9ths
Реализация аккумуляторов еще очень рано, поэтому мы не сделали много профилирования. Измерьте его и посмотрите. Я думаю, что подход, который я предложил положить успешные результаты и ошибки в один файл, а затем расщепление на следующем этапе, будет работать хорошо. –
После измерения мы обнаружили, что сохранение результата в переменной и расщепление, как описано в последнем примере, привело лишь к примерно 10% -ному разрыву в использовании памяти в конце прогона, поэтому мы будем продолжать этот подход. Выполнение второго преобразования было бы абсолютно надежным способом решения этой проблемы, из-за деталей реализации это немного больше накладных расходов, чем мы хотим понести для более позднего удобства отладки. Большое спасибо за вашу помощь. – nine9ths