Поскольку я потратил почти полный день на отладку этого, я надеюсь получить ценную информацию о 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 делает то же самое. Это должно быть проблемой в объявлении переменной...
Это только ответы на последнюю часть вопроса, но это слишком громоздко для комментариев...
У меня есть следующее объявление переменной, которое всегда возвращает 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)" />
myDict.xml
и об их использовании в XSLT.