(XSLT, оптимизация кода) Как вывести узлы, относящиеся к значению родственных узлов..?
Вопрос
Я преобразую XML в XML с помощью XSLT. Цель состоит в том, чтобы прочитать значение тега. <node1>
, если оно равно нулю, то ему должно быть присвоено значение <node2>
, если в случае <node2>
, также имеет значение NULL, тогда необходимо назначить текст по умолчанию «По умолчанию»..к обоим тегам..
РЕДАКТИРОВАТЬ: Если <node2>
является нулевым и <node1>
нет ..тогда код не должен обновляться <node2>
с 'Default'
текст, но его надо преобразовать как есть..
Это тестовый XML, который я пытаюсь использовать:
<root>
<node1></node1>
<node2></node2>
<parent>
<node1>data1</node1>
<node2></node2>
</parent>
<parent>
<node1></node1>
<node2>data2</node2>
</parent>
<parent>
<node1>data1</node1>
<node2>data2</node2>
</parent>
</root>
И это код XSLT, который я разработал:
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template name="template1" match="node2[(following-sibling::node1[.='']|preceding-sibling::node1[.=''])]">
<xsl:choose>
<xsl:when test=".=''">
<node1><xsl:text>Default</xsl:text></node1>
<node2><xsl:text>Default</xsl:text></node2>
</xsl:when>
<xsl:otherwise>
<node1>
<xsl:value-of select="text()"/>
</node1>
<xsl:copy>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="template2" match="node1[.='']"/>
Хотя мой код работает, меня не устраивает его громоздкость кода..Можно ли как-нибудь избавиться от лишних (если есть) строк....И есть ли альтернатива использованию для этого двух шаблонов (а именно шаблон1 и шаблон2), можно ли уменьшить количество шаблонов?
Решение
Я.Решение XSLT 1.0:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vReplacement">Default</xsl:variable>
<xsl:variable name="vRep" select=
"document('')/*/xsl:variable[@name='vReplacement']/text()"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node1[not(node())] | node2[../node1[not(node())]]">
<xsl:copy>
<xsl:copy-of select="../node2/text() | $vRep[not(current()/../node2/text())]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Это короче и проще чем текущие решения - на 7 строк меньше и, что еще более важно, на один шаблон меньше чем текущее выбранное решение.
Еще более важно, это решение полностью декларативное и принудительное - без вызова именованных шаблонов и единственное <xsl:apply-templates>
находится в правиле идентичности.
II.Решение XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node1[not(node())] | node2[../node1[not(node())]]">
<xsl:copy>
<xsl:sequence select="(../node2/text(), 'Default')[1]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Благодаря возможностям последовательностей XPath 2.0 это решение намного короче, чем решение XSLT 1.0..
Что-то подобное невозможно в XSLT 1.0. (например, выбор первого из объединения двух узлов без указания предикатов, чтобы сделать два узла взаимоисключающими), поскольку узел с текстом по умолчанию и узлы node1/node2 принадлежат разным документам и, как мы хорошо знаем, порядок узлов между узлами разных документов зависит от реализации и не гарантируется/не предписывается.
Это решение полностью декларативное (нет if/then/else) и полностью нажимаем стиль:единственный <xsl:apply-templates>
находится в правиле идентичности.
Другие советы
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="node1[.=''] | node2[.='']">
<xsl:copy>
<xsl:call-template name="GetOwnValue" />
</xsl:copy>
</xsl:template>
<xsl:template name="GetOwnValue">
<xsl:variable name="node2" select="following-sibling::node2[1]" />
<xsl:choose>
<xsl:when test="$node2 != ''">
<xsl:value-of select="$node2" />
</xsl:when>
<xsl:otherwise>
<xsl:text>Default</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
я изменил Томалакответил и выполнил требование ..
Как я уже упоминал в своем вопросе, этот код передает node2 как нулевой (если он равен нулю), если родственный узел node1 не равен нулю (а также если нет родственного узла node1)..
Этот код наконец-то стал альтернативой тому, который я разместил в своем вопросе..(Я не говорю, что оно достаточно идеально..но я рад, что смог попробовать..:-)
И этот код эффективнее моего где-то на 10-20 мс..:-)
Вот оно..
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="node1[.=''] | node2[.='']">
<xsl:copy>
<xsl:call-template name="GetOwnValue">
<xsl:with-param name="node">
<xsl:value-of select="name()"/>
</xsl:with-param>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template name="GetOwnValue">
<xsl:param name="node"/>
<xsl:variable name="node2" select="following-sibling::node2[1]|preceding-sibling::node2[1]" />
<xsl:variable name="node1" select="following-sibling::node1[1]|preceding-sibling::node1[1]" />
<xsl:choose>
<xsl:when test="$node2 != ''">
<xsl:value-of select="$node2" />
</xsl:when>
<xsl:when test="$node!='node1' and ($node1!='' or not(following-sibling::node1[1]|preceding-sibling::node1[1]))"/>
<xsl:otherwise>
<xsl:text>Default</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Я бы сделал это, используя XSLT 2.0, но ваш в любом случае легче читать.
<xsl:template match="node1[.='']">
<xsl:copy>
<xsl:value-of select="if (following-sibling::node2[.!='']) then following-sibling::node2[.!=''] else if (preceding-sibling::node2[.!='']) then preceding-sibling::node2[.!=''] else 'Default'"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node2[.='']">
<xsl:copy>
<xsl:value-of select="if (following-sibling::node1[.!='']) then '' else if (preceding-sibling::node1[.!='']) then '' else 'Default'"/>
</xsl:copy>
</xsl:template>