компактный XSLT-код для удаления N количества тегов, если все они нулевые - PullRequest
3 голосов
/ 24 марта 2010

Это мой ввод xml:

<root>
   <node1/>
   <node2/>
   <node3/>
   <node4/>
   <othertags/>
</root>

Выходные данные должны быть:

<root>
   <othertags/>
</root>

если любой из 4 узлов не равен нулю, то ни один из тегов не должен быть удален
пример:

<root>
   <node1/>
   <node2/>
   <node3/>
   <node4>sample_text</node4>
   <othertags/>
</root>

Тогда вывод должен быть таким же, как ввод xml.

<root>
   <node1/>
   <node2/>
   <node3/>
   <node4>sample_text</node4>
   <othertags/>
</root>

Это код XSL, который я разработал ::

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
<xsl:template match="/root/node1[.='' and ../node2/.='' and ../node3/.='' and ../node4/.='']
             |/root/node2[.='' and ../node1/.='' and ../node3/.='' and ../node4/.='']
             |/root/node3[.='' and ../node1/.='' and ../node2/.='' and ../node4/.='']
             |/root/node4[.='' and ../node1/.='' and ../node2/.='' and ../node3/.='']"/>

Как видите, код требует больше усилий и становится более громоздким по мере увеличения числа узлов. Есть ли альтернативный способ преодолеть это узкое место?

Ответы [ 4 ]

2 голосов
/ 24 марта 2010

Вы пробовали (не проверено)

<xsl:template match="node1|node2|node3|node4">
  <xsl:if test="
    (preceding-sibling::*|.|following-sibling::*)[
      self::node1 or self::node2 or self::node3 or self::node4
    ][.!='']
  ">
    <xsl:copy-of select="." />
  </if>
</xsl:template>
0 голосов
/ 25 марта 2010
<xsl:template match="/root/node()[name()='node1' or name()='node2' or name()='node3' or name()='node4']
[../node1/.='' and ../node2/.='' and ../node3/.='' and ../node4/.='']"/>
0 голосов
/ 24 марта 2010

Более точное и оптимизированное решение :

I. «Обычный» XSLT 1.0

 <xsl:variable name="vNotAllEmpty" select=
  "/*/*
      [self::node1|self::node2|self::node3|self::node4]
        [not(. = '')]

  "/>

 <xsl:template match="node()|@*" name="indent">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match=
   "/*/*/node1|/*/*/node2|/*/*/node3|/*/*/node4">
   <xsl:if test="$vNotAllEmpty">
     <xsl:call-template name="indent"/>
   </xsl:if>
 </xsl:template>
</xsl:stylesheet>
  1. Почему точнее ? Поскольку определенные шаблоны соответствуют только node{N} узлам, которые являются дочерними элементами верхнего элемента.

  2. Почему эффективнее ? Потому что тест, решающий, обрабатывать ли node{N} узлы, выполняется только один раз, а не один раз для каждого узла.

II. Использование FXSL

Библиотека FXSL имеет удобные шаблоны / функции для определения того, являются ли все или некоторые узлы в наборе узлов истинными (или условие, примененное к ним, является истинным) .

Для этой задачи наиболее подходит такой шаблон FXSL: someTrue. Вот полное преобразование:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="someTrue.xsl"/>

  <xsl:output omit-xml-declaration="yes"/>

    <xsl:variable name="vallNodes" select=
     "/*/*[starts-with(name(), 'node')]"/>

    <xsl:variable name="vNotAllEmpty">
     <xsl:call-template name="someTrue">
       <xsl:with-param name="pList"
          select="$vallNodes"/>
     </xsl:call-template>
    </xsl:variable>

 <xsl:template match="node()|@*" name="indent">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="*">
   <xsl:variable name="visInspectedNode" select=
    "count(.|$vallNodes) = count($vallNodes)"/>
   <xsl:if test=
      "not($visInspectedNode)
      or
        ($visInspectedNode and string($vNotAllEmpty))
      ">
      <xsl:call-template name="indent"/>
   </xsl:if>
 </xsl:template>
</xsl:stylesheet>

Обратите внимание на следующее :

  1. Количество обрабатываемых узлов (поддерживаемых в $vallNodes) может составлять сотни и тысячи.

  2. Решение очень расширяемое. Мы можем проверить, удовлетворяют ли все или некоторые узлы указанному условию, используя соответственно шаблоны FXSL allTrueP и someTrueP.

  3. Логика сканирования всех узлов и накопления значения истинности встроена в эти шаблоны, и вы никогда не закодируете эту логику - следовательно, не тратится дополнительное время и нет возможности совершать ошибки.

0 голосов
/ 24 марта 2010

Режим «фильтр-те-узлы» проверяет условие, а «не-те-узлы» удаляет нежелательные узлы. Нефильтрованные узлы будут соответствовать одному из немодальных шаблонов применения, как если бы мы только что сказали «внутри корневого» шаблона.

<xsl:template match="root">
    <xsl:copy>
        <xsl:apply-templates mode="filter-those-nodes"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="*" mode="filter-those-nodes">
    <xsl:choose>
        <xsl:when test="'' = concat(node1, node2, node3, node4)">
            <xsl:apply-templates mode="filter-those-nodes"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:apply-templates/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="node1 | node2 | node3 | node4" mode="not-those-nodes">
    <!-- filtered out -->
</xsl:template>

<xsl:template match="*" mode="not-those-nodes">
    <xsl:apply-templates select="." />
</xsl:template>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...