Я создаю файл csv, используя приведенный ниже xsl. Мне нужно выполнить проверку условия: если XML не содержит элемента элемента, мне нужно добавить строку по умолчанию, в противном случае необходимо добавить элементы из элемента.
Мой XML большой, и я использую потоковую передачу SAXON. Когда я использую элементы if и for-each, он выдает ошибки. Мне нужна твоя помощь, чтобы справиться с этим.
Произошла ошибка: Произошла ошибка при преобразовании: Правило шаблона не поддерживает потоковую передачу.
XSLT
<?xml version = "1.0"?>
<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "3.0">
<xsl:output method = "text" encoding = "UTF-8" indent = "no" omit-xml-declaration = "yes"/>
<xsl:mode streamable = "yes"/>
<xsl:strip-space elements = "*"/>
<xsl:variable name = "dq" select = "'"'"/>
<xsl:variable name = "qcq" select = "'","'"/>
<xsl:variable name = "lf" select = "' '"/>
<xsl:variable name = "email" select = "'[email protected]'"/>
<xsl:variable name = "product_id" select = "'product1'"/>
<xsl:template match = "items">
<xsl:text>product_id,email,key</xsl:text>
<xsl:value-of select = "' '"/>
<xsl:choose>
<xsl:when test = "not(item)">
<xsl:value-of disable-output-escaping = "yes" select = "concat($dq,$qcq,$email,$qcq,$dq,$lf)"/>
</xsl:when>
<xsl:when test = "item">
<xsl:for-each select = "item ! copy-of(.)">
<xsl:variable name = "p_key" select = "id"/>
<xsl:value-of disable-output-escaping = "yes" select = "concat($dq,$product_id,$qcq,$email,$qcq,$p_key,$dq,$lf)"/>
</xsl:for-each>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Sample XML
<?xml version = "1.0" encoding = "UTF-8"?>
<items><item><id><![CDATA[040411c47702b29acb4e4120040ee6b937fbb8]]></id></item></items>
no item in xml
<?xml version = "1.0" encoding = "UTF-8"?>
<items></items>
Спасибо, @MartinHonnen Да, элемент items совершенно пуст.
Если элемент items
может быть пустым, вы можете использовать, например.
<xsl:template match = "items">
<xsl:text>product_id,email,key</xsl:text>
<xsl:value-of select = "' '"/>
<xsl:choose>
<xsl:when test = "not(has-children())">
<xsl:value-of select = "concat($dq,$qcq,$email,$qcq,$dq,$lf)"/>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select = "item ! copy-of(.)">
<xsl:variable name = "p_key" select = "id"/>
<xsl:value-of select = "concat($dq,$product_id,$qcq,$email,$qcq,$p_key,$dq,$lf)"/>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Я удалил экранирование отключение-вывода, поскольку, на мой взгляд, оно не имеет смысла для текста метода вывода.
Другой вариант: on-empty
:
<xsl:template match = "items">
<xsl:text>product_id,email,key</xsl:text>
<xsl:value-of select = "' '"/>
<div>
<xsl:for-each select = "item ! copy-of(.)">
<xsl:variable name = "p_key" select = "id"/>
<xsl:value-of select = "concat($dq,$product_id,$qcq,$email,$qcq,$p_key,$dq,$lf)"/>
</xsl:for-each>
<xsl:on-empty>
<xsl:value-of select = "concat($dq,$qcq,$email,$qcq,$dq,$lf)"/>
</xsl:on-empty>
</div>
</xsl:template>
При использовании метода вывода text
элемент div
, конечно, не производит вывод, возможно, вместо него можно было бы использовать xsl:sequence
.
Спасибо @martin, использование функции has-children работает для меня. Ценю ваш ответ.
Основная проблема с has-children()
заключается в том, что он вернет true, если элемент содержит текстовый узел с пробелами или содержимое комментария. Если это подходит для ваших случаев использования, это нормально, но я бы предпочел xsl:on-empty
здесь. Вы можете заменить <div>..</div>
на <xsl:value-of>....</xsl:value-of>
.
Еще одно решение — использовать xsl:fork
. Что-то вроде этого:
<xsl:template match = "items">
<xsl:text>product_id,email,key</xsl:text>
<xsl:value-of select = "' '"/>
<xsl:fork>
<xsl:sequence>
<xsl:if test = "not(item)">
<xsl:value-of select = "concat($dq,$qcq,$email,$qcq,$dq,$lf)"/>
</xsl:if>
</xsl:sequence>
<xsl:sequence>
<xsl:for-each select = "item ! copy-of(.)">
<xsl:variable name = "p_key" select = "id"/>
<xsl:value-of select = "concat($dq,$product_id,$qcq,$email,$qcq,$p_key,$dq,$lf)"/>
</xsl:for-each>
</xsl:sequence>
</xsl:fork>
</xsl:template>
Не испытано.
Еще одно решение — включить элемент «стоппер» в каждую итерацию:
<xsl:variable name = "stopper" as = "element(*)"><stopper/></xsl:variable>
<xsl:for-each select = "item ! copy-of(.), $stopper">
<xsl:choose>
<xsl:when test = "self::stopper">
<xsl:if test = "position()=1">
<xsl:value-of select = "concat($dq,$product_id,$qcq,$email,$qcq,$p_key,$dq,$lf)"/>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:variable name = "p_key" select = "id"/>
<xsl:value-of select = "concat($dq,$product_id,$qcq,$email,$qcq,$p_key,$dq,$lf)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
Тоже не тестировался.
Будет ли элемент
items
полностью пустым, если нет элементов (по умолчанию)? Существует потоковая функция просмотра впередhas-children
, которую вы можете использовать w3.org/TR/xpath-functions-31/#func-has-children.