XSLT: использование переменных в ключевой функции - PullRequest
2 голосов
/ 27 января 2012

У меня есть следующий XML-файл:

<titles>
   <book title="XML Today" author="David Perry"/>
   <book title="XML and Microsoft" author="David Perry"/>
   <book title="XML Productivity" author="Jim Kim"/>
   <book title="XSLT 1.0" author="Albert Jones"/>
   <book title="XSLT 2.0" author="Albert Jones"/>
   <book title="XSLT Manual" author="Jane Doe"/>
</titles>

Я хочу исключить некоторые элементы и применить следующий XSLT:

    <?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">

  <xsl:output method="xml" indent="yes"/>

  <xsl:key name="author1-search" match="book[starts-with(@author, 'David')]" use="@title"/>
  <xsl:template match="book [key('author1-search', @title)]" />

  <xsl:key name="author2-search" match="book[starts-with(@author, 'Jim')]" use="@title"/>
  <xsl:template match="book [key('author2-search', @title)]" />

  <xsl:template match="/">  
    <xsl:apply-templates />
  </xsl:template>

</xsl:stylesheet>

Можно ли использовать встроенную переменную xsl

  <xsl:variable name="Author"> 
    <name>David</name> 
    <name>Jim</name> 
  </xsl:variable> 

вместо «author1-search», «author2-search» и т. Д. Для циклического перебора имен?

Я могу использовать только XSLT 1.0 (2.0 в настоящее время не поддерживается).

Заранее спасибо,

Лев

Ответы [ 3 ]

6 голосов
/ 27 января 2012

Нет, шаблоны (в XSLT 1.0) не могут содержать ссылки на переменные / параметры .

Один из способов выполнить такую ​​задачу будет выглядеть так: :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:param name="pAuthor" select="'David Perry'"/>

    <xsl:key name="kBookaByAuthor" match="book"
             use="@author"/>

    <xsl:template match="/">

     Books written by <xsl:value-of select="$pAuthor"/> :<xsl:text/>
        <xsl:apply-templates
             select="key('kBookaByAuthor', $pAuthor)"/>
    </xsl:template>

    <xsl:template match="book">
     <!-- One can do more formatting here -->
     <xsl:text>&#xA;     </xsl:text>
     <xsl:value-of select="@title"/>
    </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к предоставленному документу XML :

<titles>
    <book title="XML Today" author="David Perry"/>
    <book title="XML and Microsoft" author="David Perry"/>
    <book title="XML Productivity" author="Jim Kim"/>
    <book title="XSLT 1.0" author="Albert Jones"/>
    <book title="XSLT 2.0" author="Albert Jones"/>
    <book title="XSLT Manual" author="Jane Doe"/>
</titles>

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

Books written by David Perry :
  XML Today
  XML and Microsoft

Обновление : в комментарии ОП уточняется, что:

" Я думал, что полностью указал свои требования в первоначальном вопросе. Как я уже упоминал в своем вопросе и в моем первом комментарии, это будет мне полезно увидеть подход для решения более чем одного автор"

Вот решение, которое действительно использует ключи (обратите внимание, что «ключ» в ответе @ Flynn1179 не создает никакого индекса и является просто постоянной последовательностью строк - таким образом, функция key(), использующая этот xsl:key, фактически находит строку в списке строк - что является O (N), в отличие от, как правило, O (1) для поиска в истинном индексе):

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:ext="http://exslt.org/common">

     <xsl:output method="text"/>

     <xsl:param name="pAuthors">
      <x>David Perry</x>
      <x>Jane Doe</x>
     </xsl:param>

     <xsl:variable name="vParams" select=
      "ext:node-set($pAuthors)/*"/>

     <xsl:key name="kBookByAuthor" match="book"
                     use="@author"/>

     <xsl:template match="/">
       Books written by : <xsl:text/>

       <xsl:apply-templates select="$vParams"/>

       <xsl:apply-templates select=
        "key('kBookByAuthor', $vParams)"/>
     </xsl:template>

     <xsl:template match="book">
       <!-- One can do more formatting here -->
       <xsl:text>&#xA;     </xsl:text>
       <xsl:value-of select="concat('&quot;', @title, '&quot;')"/>
     </xsl:template>

     <xsl:template match="x">
      <xsl:if test="not(position() = 1)">, </xsl:if>
      <xsl:value-of select="."/>
     </xsl:template>
</xsl:stylesheet>

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

   Books written by : David Perry, Jane Doe
     "XML Today"
     "XML and Microsoft"
     "XSLT Manual"

Примечание : В этом решении используется функция Exslt node-set(). Здесь это сделано только для удобства . При реальном использовании значение параметра указывается извне, и тогда функция ext:node-set() не требуется.

Эффективность : Это решение использует истинную силу ключей в XSLT. Эксперимент, проведенный с использованием процессоров XSLT MSXML (3, 4 и 6), показывает, что при поиске 10000 авторов время преобразования с различными процессорами XSLT варьируется от: 32 мс до 45 мс.

Интересно, что решение, представленное @ Flynn1179, на самом деле не делает ключевой индекс, и для многих процессоров XSLT оно занимает (для того же числа (10000) авторов) от 1044мс до 5564мс :

MSXML3 : 5564 мс,

MSXML4 : 2526мс,

MSXML6 : 4867 мс,

AltovaXML : 1044мс.

Это значительно ниже производительности, которую можно получить при индексировании по настоящему ключу (от 32 до 45 мс).

2 голосов
/ 27 января 2012

Вместо того, чтобы использовать переменные, вы можете просто включить элемент в свой лист XSLT в его собственное пространство имен и ссылаться на него, например так:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">
  <xsl:key name="authors" use="document('')/*/my:authors/my:name" match="/" />

  <my:authors> 
    <my:name>David Perry</my:name>
    <my:name>Jim Kim</my:name> 
  </my:authors>

  <xsl:template match="book[not(key('authors',@author))]" />

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

Шаблон book соответствует тем, которые НЕиметь соответствующий элемент my:name для своего автора и ничего не выводит.Шаблон идентичности выводит все остальное, включая элементы книги, которые вас интересуют.Ключ немного взломан, он по существу соответствует всему документу, в котором существует имя, а не соответствует элементу my:name, который соответствует.Поскольку вы заботитесь только о его существовании, это не должно быть проблемой.

В качестве альтернативы, если вы предпочитаете передавать список авторов, вы можете использовать это:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:param name="authors" select="'David Perry,Jim Kim'" />

  <xsl:template match="book">
    <xsl:if test="contains(concat(',',$authors,','),concat(',',@author,','))">
      <xsl:call-template name="identity" />
    </xsl:if>
  </xsl:template>

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

Переменная используется в <xsl:if>, а не в сопоставлении с шаблоном, но выполняет ту же работу.Для этого конкретного кода требуется список авторов, указанный в виде списка через запятую, но его достаточно легко адаптировать, если вы предпочитаете использовать другой формат.

2 голосов
/ 27 января 2012

Шаблоны в XSLT 1.0 не могут содержать ссылки на переменные или параметры, поэтому вы не можете использовать ссылки на переменные или параметры в этих определениях ключей или в имеющихся у вас атрибутах соответствия шаблонов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...