(XSLT, оптимизация кода) Как вывести узлы, относящиеся к значению родственных узлов..?

StackOverflow https://stackoverflow.com/questions/2287740

  •  21-09-2019
  •  | 
  •  

Вопрос

Я преобразую 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>
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top