Xalan и функция document ()

1

Поскольку я потратил почти полный день на отладку этого, я надеюсь получить ценную информацию о SO по следующей проблеме:

Я запускаю преобразование XSL во входном документе, моя таблица стилей загружает внешний XML-документ, который содержит значения поиска. Мне нужно сделать некоторые сравнения. Я загружаю внешний документ следующим образом:

<xsl:variable name="dictionary"
    select="document('myDict.xml', document(''))/path/to/LookupElement" />

LookupElement - это элемент, который содержит полный XML-фрагмент, который мне нужен. Всюду по таблице стилей различные выражения сравнения обращаются к $dictionary. Теперь, что происходит, что преобразование с вызовом функции document() на месте занимает около 12 (!) Минут, используя Xalan (2.7.?, Последнюю версию, загруженную с сайта Apache, а не тот, который содержится в JRE).

Та же таблица стилей без вызова document() (и без моих сравнений с доступом к данным в $dictionary) завершается за считанные секунды.

Та же таблица стилей, использующая Saxon-B 9.1.0.8, завершается за считанные секунды.

Информация: Внешний документ имеет 25 МБ (!), И у меня нет возможности уменьшить его размер. Я выполняю преобразования с помощью xslt-Task муравья под JRE 6.

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

<xsl:variable name="myAttExists" select="boolean($dictionary/path/to/@myAttribute)"/>

Я в конце своего ума. Я знаю, что Xalan правильно читает документ, все ссылки идут на $dictionary, поэтому я не звоню в document() несколько раз.

Кто-нибудь есть идея?

Редактировать:

Я удалил ссылку на XML-схему из внешнего XML-документа, чтобы предотвратить просмотр схем Xalan или базовый (Xerces) Parser.

Редактировать:

Я проверил, что myAttExists всегда будет true, даже если указать имя атрибута, которое, myAttExists, не существует во всем внешнем XML-документе. Я даже изменил выражение выше:

<xsl:variable name="myAttExists" select="count($dictionary/path/to/@unknownAttribute) != 0"/>

который все еще дает true.

Изменить:

Я удалил вызов функции document() и всех ссылок на $dictionary для тестирования. Это уменьшает время выполнения преобразования с Xalan до 16 секунд.

Редактировать:

Интересная деталь: версия Xalan, поставляемая с Oxygen 12.1, завершает в течение нескольких секунд загрузку внешнего XML-документа. Однако он также неправильно оценивает наличие атрибутов...

Редактировать:

У меня есть следующее объявление переменной, которое всегда возвращает true:

<xsl:variable name="expectedDefaultValueExists">
    <xsl:choose>
        <xsl:when test="@index">
            <xsl:value-of select="boolean($dictionary/epl:Object[@index = $index]/@defaultValue)"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="boolean($dictionary/epl:Object[@index = $index]/epl:SubObject[@subIndex = $subIndex]/@defaultValue)"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:variable>

Возможно ли это в XSLT/XPath 1.0? $index и $subIndex вычисляются из @index и @subIndex контекстного узла. Я хочу загрузить атрибут defaultValue из внешнего XML-документа, который имеет равный индекс и/или subIndex. Можно ли использовать переменные в предикатах в XPath 1.0? Это работает в XPath 2.0. Что касается неправильного присваивания переменной, я больше не верю в проблему с парсером (Xalan), так как PHP XSLTProcessor делает то же самое. Это должно быть проблемой в объявлении переменной...

  • 0
    Хорошо, если вы знаете, что Saxon работает нормально и с хорошей производительностью, тогда зачем вам Xalan и XSLT 1.0, поскольку вы используете версию Xalan, а не JRE, вы все равно используете стороннюю библиотеку и можете перейти непосредственно к Saxon 9. Конечно термин LookupElement предполагает, что определение и использование ключа может повысить производительность, вам необходимо показать нам более подробную информацию о структуре XML и myDict.xml и об их использовании в XSLT.
  • 0
    @MartinHonnen Мне нужно поддерживать версии моих таблиц стилей XSLT 1.0 и 2.0, потому что они используются в разных средах. Иначе я бы уже отказался от версии 1.0. Я указываю переменную непосредственно на элемент, который содержит все данные, которые мне нужны. Поскольку это прямой XPath, я не понимаю, как могут возникнуть проблемы с производительностью?
Показать ещё 9 комментариев
Теги:
xpath
xslt
xalan

1 ответ

1
Лучший ответ

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

У меня есть следующее объявление переменной, которое всегда возвращает true при использовании в качестве test xsl:if или xsl:when:

<xsl:variable name="expectedDefaultValueExists">
    <xsl:choose>
        <xsl:when test="@index">
            <xsl:value-of select="boolean($dictionary/epl:Object[@index = $index]/@defaultValue)"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="boolean($dictionary/epl:Object[@index = $index]/epl:SubObject[@subIndex = $subIndex]/@defaultValue)"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:variable>

В XSLT 1.0 переменная с телом, а не с select всегда становится "фрагментом результирующего дерева", в данном случае с единственным текстовым узлом, который будет содержать строку "true" или "false", если это необходимо. Любой непустой RTF считается истинным при преобразовании в boolean.

В XSLT 2.0 аналогичная история - 2.0 не различает узловые множества и фрагменты дерева результатов, но все же переменная будет "временным деревом" с единственным дочерним элементом текстового узла, значением которого является строка "true" или "false",, и оба эти дерева истинны при преобразовании в boolean. Если вы хотите получить фактическое логическое значение из переменной, вам нужно изменить две вещи: добавьте as="xs:boolean" в объявление переменной и используйте xsl:sequence вместо xsl:value-of:

<xsl:variable name="expectedDefaultValueExists" as="xs:boolean">
    <xsl:choose>
        <xsl:when test="@index">
            <xsl:sequence select="boolean($dictionary/epl:Object[@index = $index]/@defaultValue)"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:sequence select="boolean($dictionary/epl:Object[@index = $index]/epl:SubObject[@subIndex = $subIndex]/@defaultValue)"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:variable>

Команда xsl:value-of a преобразует результат ее select в строку и создает текстовый узел, содержащий эту строку. Команда xsl:sequence просто возвращает значение из select напрямую, как бы оно ни было.

Но есть более простые способы добиться того же. В XPath 2.0 вы можете сделать, если /then/else создает непосредственно в XPath

<xsl:variable name="expectedDefaultValueExists"
   select="if (@index)
           then $dictionary/epl:Object[@index = $index]/@defaultValue
           else $dictionary/epl:Object[@index = $index]/epl:SubObject[@subIndex = $subIndex]/@defaultValue" />

В 1.0 вам нужно быть немного более креативным

<xsl:variable name="expectedDefaultValueExists"
   select="(@index and $dictionary/epl:Object[@index = $index]/@defaultValue)
        or (not(@index) and $dictionary/epl:Object[@index = $index]/epl:SubObject[@subIndex = $subIndex]/@defaultValue)" />
  • 0
    Это отличное объяснение. Я также не знал о упомянутом синтаксисе XPath 2.0, решение Xpath 1.0 также намного лучше, чем я придумал. Спасибо!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню