xslt преобразовать список и взять максимум - PullRequest
1 голос
/ 06 марта 2012

Я хочу:

  1. выберите все атрибуты "foo", которые являются строковыми значениями, и сохраните значения в списке.
  2. преобразовать каждое значение атрибута "foo" в этом списке, используя некоторую карту в моем xslt, в число.
  3. выберите максимальное значение из списка и выведите его.

Итак, учитывая следующее xml:

<t>
    <tag foo="A">apples</tag>
    <tag foo="C">oranges</tag>
    <tag foo="B">trees</tag>
</t>

И следующее отображение:

<xsl:variable name="myMap">
    <entry key="A">1</entry>
    <entry key="B">2</entry>
    <entry key="C">3</entry>
</xsl:variable>

Вывод будет:

<max>3</max>

Еще один вопрос, почему я не могу сделать отступ для своего кода? Я ставлю пробелы, но это не работает.

1 Ответ

3 голосов
/ 06 марта 2012

I Это стандартное преобразование 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:strip-space elements="*"/>

 <xsl:variable name="vrtfMap">
  <entry key="A" value="1"/>
  <entry key="B" value="2"/>
  <entry key="C" value="3"/>
  <entry key="X" value="8"/>
 </xsl:variable>

 <xsl:variable name="vMap" select=
  "document('')/*/xsl:variable[@name = 'vrtfMap']/*"/>

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

 <xsl:template match="@foo">
  <xsl:attribute name="foo">
   <xsl:value-of select="$vMap[@key = current()]/@value"/>
  </xsl:attribute>
 </xsl:template>
</xsl:stylesheet>

при применении к следующему документу XML (поскольку вы не предоставили его):

<t foo="X">
    <a foo="A">
        <b foo="B"/>
    </a>
    <c foo="C"/>
</t>

дает желаемый, правильный результат :

<t foo="8">
   <a foo="1">
      <b foo="2"/>
   </a>
   <c foo="3"/>
</t>

Пояснение : правильное использование функции XSLT current().


II. Решение 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:strip-space elements="*"/>

 <xsl:key name="kValFromKey" match="entry/@value" use="../@key"/>

 <xsl:variable name="vrtfMap">
  <entry key="A" value="1"/>
  <entry key="B" value="2"/>
  <entry key="C" value="3"/>
  <entry key="X" value="8"/>
 </xsl:variable>

 <xsl:variable name="vMap" select=
  "document('')/*/xsl:variable[@name = 'vrtfMap']/*"/>

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

 <xsl:template match="@foo">
  <xsl:variable name="vCur" select="."/>

  <xsl:attribute name="foo">
   <xsl:for-each select="document('')">
    <xsl:value-of select="key('kValFromKey', $vCur)"/>
   </xsl:for-each>
  </xsl:attribute>
 </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к тому же XML-документу (см. Выше), получается тот же правильный результат .

Объяснение

  1. Использование <xsl:for-each select="document('')"> для установки текущего документа в таблицу стилей, чтобы функция key() использовала индекс ключа, созданный для этого документа.

  2. Сохранение узла, соответствующего шаблону, в переменной, так что мы можем использовать его внутри xsl:for-each - current(), не может быть правильно использовано здесь, потому что он получает текущий узел, на котором работает xsl:for-each .

ОБНОВЛЕНИЕ : ОП пояснил в комментарии, что его самая большая проблема - найти максимум.

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>

     <xsl:variable name="vrtfMap">
      <entry key="A" value="1"/>
      <entry key="B" value="2"/>
      <entry key="C" value="3"/>
     </xsl:variable>

     <xsl:variable name="vMap" select=
      "document('')/*/xsl:variable[@name = 'vrtfMap']/*"/>

     <xsl:template match="/">
     <xsl:variable name="vDoc" select="."/>

      <xsl:variable name="vFoosMapped"
           select="$vMap[@key = $vDoc/*/*/@foo]"/>
      <max>
       <xsl:value-of select=
         "$vFoosMapped
            [not($vFoosMapped/@value > @value)]
              /@value
         "/>
      </max>
     </xsl:template>
</xsl:stylesheet>

При наличии данного XML-документа (в документе, представленном OP, отсутствует элемент top верхнего уровня):

<t>
    <tag foo="A">apples</tag>
    <tag foo="C">oranges</tag>
    <tag foo="B">trees</tag>
</t>

желаемый, правильный результат выдается :

<max>3</max>

Примечание : Более эффективный способ вычисления максимума (или минимума - аналогичным образом) в 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:strip-space elements="*"/>

     <xsl:variable name="vrtfMap">
      <entry key="A" value="1"/>
      <entry key="B" value="2"/>
      <entry key="C" value="3"/>
     </xsl:variable>

     <xsl:variable name="vMap" select=
      "document('')/*/xsl:variable[@name = 'vrtfMap']/*"/>

     <xsl:template match="/">
     <xsl:variable name="vDoc" select="."/>

      <xsl:variable name="vFoosMapped"
           select="$vMap[@key = $vDoc/*/*/@foo]"/>
      <max>
       <xsl:for-each select="$vFoosMapped">
         <xsl:sort select="@value" data-type="number" order="descending"/>

         <xsl:if test="position() = 1">
          <xsl:value-of select="@value"/>
         </xsl:if>
       </xsl:for-each>
      </max>
     </xsl:template>
</xsl:stylesheet>

Еще один вопрос, почему я не могу сделать отступ для своего кода? Я ставлю пробелы но это не работает.

Это ТАКАЯ ошибка, которую они не могли исправить в течение многих месяцев.

Скорее всего, вы используете IE. Если у вас версия 9, выполните следующие действия: :

  1. Нажмите F12.

  2. В появившемся окне нажмите на крайнее правое меню и выберите: «Режим документа: Стандарты IE9»

Теперь вы должны увидеть код с отступом.

...