Вставьте набор узлов XSLT B в другой набор узлов A в обнаруженной позиции внутри текстового узла набора узлов A - PullRequest
1 голос
/ 18 октября 2011

Я использую XSLT для преобразования сложного XML-вывода системы управления контентом в XHTML. Я использую <xsl:apply-templates/>, чтобы получить XHTML-фрагмент того, что описано входными данными XML. Этот ввод XML имеет очень сложную структуру, которая может описывать множество различных случаев, которые должны обрабатываться несколькими элементами шаблона XSLT. И эта структура может изменяться довольно часто в будущем.

Ранее полученный фрагмент этого преобразования был напрямую отправлен на выход XSLT. Теперь требования изменились, и мне нужно зафиксировать результат, иногда изменять его, чтобы вставить какой-то другой правильно сформированный фрагмент XHTML в определенную позицию в значении фрагмента.

Для демонстрации рассмотрим <xsl:apply-templates/>, создав некоторый непрозрачный фрагмент XHTML, захваченный в переменную контейнер .

<xsl:variable name="container">
  <xsl:apply-templates />
</xsl:variable>

Далее в переменной есть второй XHTML-фрагмент фрагмент :

<xsl:variable name="snippet">
  <xsl:call-template name="get-snippet" />
</xsl:variable>

Требование гласит, что в $ snippet должен быть установлен набор узлов перед любым произвольно содержащимся периодом в конце значения $ container . Это не проблема, если фрагменты XHTML в обеих переменных не должны быть сохранены как фрагменты. Таким образом, нельзя работать со строковыми значениями любой переменной.

Есть ли возможность выполнить это требование в XSLT без потери мощности и гибкости <xsl:apply-templates/> при извлечении фрагмента XHTML в $ контейнере ?

Кстати: я уже знаю о доступе к последнему текстовому узлу в $container, используя:

node-set($container)//child::text[last()] 

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

1 Ответ

2 голосов
/ 19 октября 2011

I.Решение XSLT 1.0 :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
 exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vrtfContainer">
  <html>
   <p>Hello, world.</p>
   <p> This is <b>just</b> a <i>demo.</i></p>
   <p> Of some text</p>
  </html>
 </xsl:variable>

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

 <xsl:variable name="vrtfSnippet">
   <p>Snippet</p>
 </xsl:variable>

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

  <xsl:variable name="vText" select=
  "($vContainer//text()[contains(.,'.')])[last()]"/>

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

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

 <xsl:template match="text()">
  <xsl:choose>
   <xsl:when test="not(generate-id() = generate-id($vText))">
    <xsl:value-of select="."/>
   </xsl:when>
   <xsl:otherwise>
     <xsl:call-template name="insertSnippet">
      <xsl:with-param name="pText" select="$vText"/>
     </xsl:call-template>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

 <xsl:template name="insertSnippet">
  <xsl:param name="pText"/>

    <xsl:copy-of select="substring-before($pText, '.')"/>

    <xsl:variable name="vTail" select=
         "substring-after($pText, '.')"/>

  <xsl:choose>
   <xsl:when test="not(contains(substring($vTail,2), '.'))">
    <xsl:copy-of select="$vSnippet"/>
    <xsl:value-of select="concat('.', $vTail)"/>
   </xsl:when>
   <xsl:otherwise>
    <xsl:text>.</xsl:text>
    <xsl:call-template name="insertSnippet">
     <xsl:with-param name="pText"
      select="substring-after($pText, '.')"/>
    </xsl:call-template>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

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

<html>
   <p>Hello, world.</p>
   <p> This is <b>just</b> a <i>demo
         <p>Snippet</p>.</i></p>
   <p> Of some text</p>
</html>

Объяснение : Правило идентификации переопределено рекурсивным именованным шаблоном для поиска последнего '.' в строке.


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:variable name="vContainer">
  <html>
   <p>Hello, world.</p>
   <p> This is <b>just</b> a <i>demo.</i></p>
   <p> Of some text</p>
  </html>
 </xsl:variable>


 <xsl:variable name="vSnippet">
   <p>Snippet</p>
 </xsl:variable>

  <xsl:variable name="vText" select=
  "($vContainer//text()[contains(.,'.')])[last()]"/>

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

 <xsl:template match="/">
  <xsl:apply-templates select="$vContainer/*"/>
 </xsl:template>

 <xsl:template match="text()[. is $vText]">
  <xsl:variable name="vInd" select=
   "index-of(string-to-codepoints(.), string-to-codepoints('.'))[last()]"/>
  <xsl:sequence select="substring(., 1, $vInd -1)"/>
  <xsl:sequence select="$vSnippet/*"/>
  <xsl:sequence select="substring(., $vInd)"></xsl:sequence>
 </xsl:template>
</xsl:stylesheet>

При выполнении этого преобразования XSLT 2.0 снова будет получен требуемый правильный результат :

<html>
   <p>Hello, world.</p>
   <p> This is <b>just</b> a <i>demo
         <p>Snippet</p>.</i></p>
   <p> Of some text</p>
</html>

Пояснение : использование стандартных функций XPath 2.0 string-to-codepoints(), index-of(), substring() и оператора is.

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