Прерывание потока на выходе XSLT - PullRequest
0 голосов
/ 20 ноября 2011

Как я могу сделать XSLT, чтобы преобразовать это:

<root>
 <element>This
  <element>is</element>
 </element>
 <element>normal!</element>
 <element>This</element>
 <element>will
  <special>break here</special>
  <element>and
   <element>also
    <special>here!</special>
   </element>
  </element>
 </element>
</root>

В это:

<root>
 <content>This is normal! This will</content>
 <special>break here</special>
 <content>and also</content>
 <special>here!</special>
</root>

Учитывая, что <special> может появляться любое количество раз и в любом месте. Я думаю, что мне нужно обработать файл два раза, чтобы сделать это, но мне нужно сделать это сразу.

РЕДАКТИРОВАТЬ : просто пояснение, это не простая копия, содержимое также преобразуется с одного конца в другой (вот почему я подумал, что мне нужно два разных преобразования).

EDIT2 : вот алгоритм, который я хочу:

1) Продолжайте обрабатывать все элементы, пока не найдете какой-то особенный элемент, поместите все это вглубь <content>

2) Если следующий элемент особенный, поместите его внутрь <specialContent>

3) Если что-то осталось, перейдите к шагу 1.

EDIT3 Я изменил свой пример xml, чтобы сделать его более понятным.

Ответы [ 5 ]

2 голосов
/ 20 ноября 2011

Похоже, что все существующие в настоящее время ответы натыкаются на этот документ XML :

<root>
    <element>This   
        <element>is</element></element>
    <element>normal!</element>
    <element>This</element>
    <element>will   
        <special>break here</special>
        <element>and    
            <element>also     
                <special>here!</special>
            </element>
        </element>
        <element>But not here</element>
    </element>
</root>

Вот преобразование, которое обрабатывает его правильно :

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

 <xsl:key name="content" match="element"
  use="generate-id((ancestor::special|preceding::special)[last()])"/>

 <xsl:template match="/*">
   <xsl:copy>
    <content>
      <xsl:apply-templates select="key('content', '')"/>
    </content>
    <xsl:apply-templates select="//special" />
   </xsl:copy>
 </xsl:template>

 <xsl:template match="special">
   <xsl:copy-of select="." />
   <content>
    <xsl:apply-templates select=
       "key('content', generate-id())" />
   </content>
 </xsl:template>

 <xsl:template match="element">
   <xsl:value-of select="normalize-space(text())" />
   <xsl:text> </xsl:text>
 </xsl:template>
</xsl:stylesheet>

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

<root>
   <content>This is normal! This will </content>
   <special>break here</special>
   <content>and also </content>
   <special>here!</special>
   <content>But not here </content>
</root>
0 голосов
/ 21 ноября 2011

Ответ Дмитрия был очень хорошим, но он кое-что сломал в моем XSLT из-за сгенерированного контента и очень глубокой иерархии шаблонов.

Вот решение, которое я использую:

Сначала я делаю всю обычную обработку, затем вызываю пост-процесс:

<xsl:variable name="content">
   <xsl:apply-templates />
</xsl:variable>
<xsl:apply-templates select="msxsl:node-set($content)" mode="postProcess" />

В пост-процессе он ищет все, что не является div с тегом WideContent в его атрибуте класса, и группирует соседних братьев и сестер внутри другого div, те, которые с тегом WideContent просто копируются (но также могут быть также сгруппировать таким же образом).

<xsl:template match="/*[not(self::xhtml:div[contains(@class,'WideContent')])][preceding-sibling::*[1][self::xhtml:div[contains(@class,'WideContent')]] or position()=1]" mode="postProcess">
  <div class="NormalContent">
    <xsl:call-template name="postProcessNormalContent" />
  </div>
</xsl:template>

<xsl:template name="postProcessNormalContent" match="/*[not(self::xhtml:div[contains(@class,'WideContent')])]" mode="postProcessNormalContent">
  <xsl:copy-of select="."/>
  <xsl:apply-templates select="following-sibling::*[1]" mode="postProcessNormalContent" />
</xsl:template>

<xsl:template match="xhtml:div[contains(@class,'WideContent')]" mode="postProcess">
  <xsl:copy-of select="."/>
</xsl:template>

<xsl:template match="node()" mode="postProcess" />
<xsl:template match="node()" mode="postProcessNormalContent" />

Любые предложения по улучшению, которые будут с радостью приняты.

0 голосов
/ 20 ноября 2011
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="root">
        <content>
            <xsl:copy-of select=".//content/text()[following-sibling::node()[1][self::subcontent]]" />
            <xsl:copy-of select=".//subcontent/text()[following-sibling::node()[1][self::subsubcontent]]" />         
            <xsl:copy-of select=".//subsubcontent/text()[following-sibling::node()[1][self::SpecialContent]]" />               
        </content>
        <xsl:for-each select=".//SpecialContent">
            <xsl:copy-of select="." />
        </xsl:for-each>
        <content>
            <xsl:copy-of select=".//subsubcontent/text()[preceding-sibling::node()[1][self::SpecialContent]]" />  
            <xsl:copy-of select=".//subcontent/text()[preceding-sibling::node()[1][self::subsubcontent]]" />
            <xsl:copy-of select=".//content/text()[preceding-sibling::node()[1][self::subcontent]]" />
        </content>
    </xsl:template>
    <xsl:template match="text()" />
</xsl:stylesheet>

не очень красиво, но должно работать с другими преобразованиями, если таковые имеются

0 голосов
/ 20 ноября 2011

Я думаю, что один из способов добиться этого - сгруппировать все элементы element * по первому ** специальному элементу, который является потомком или следующим элементом,

<xsl:key name="content" 
   match="element" 
   use="generate-id((descendant::special|following::special)[1])" />
* 1005.* Затем можно начать с сопоставления всех специальных элементов в документе
 <xsl:apply-templates select="//special" />

И для каждого соответствующего элемента special вы можете затем сгруппировать их вместевсе связанные элементы element , у которых этот текущий элемент является их первым потомком или следующим элементом.

<content>
   <xsl:apply-templates select="key('content', generate-id())" />
</content>

Итак, с учетом следующего XSLT

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

   <xsl:key name="content" 
      match="element" 
      use="generate-id((descendant::special|following::special)[1])" />

   <xsl:template match="/root">
      <xsl:copy>
         <xsl:apply-templates select="//special" />
      </xsl:copy>
   </xsl:template>

   <xsl:template match="special">
      <content>
         <xsl:apply-templates select="key('content', generate-id())" />
      </content>
      <xsl:copy-of select="." />
   </xsl:template>

   <xsl:template match="element">
      <xsl:value-of select="normalize-space(text())" /><xsl:text> </xsl:text>
   </xsl:template>
</xsl:stylesheet>

При примененииДля примера XML выдается следующее:

<root>
   <content>This is normal! This will </content>
   <special>break here</special>
   <content>and also </content>
   <special>here!</special>
</root>

(обратите внимание, что в конце каждого тега содержимого есть пробел, но я мог бы легко настроить XSLT, чтобы удалить его, если вам нужно).

0 голосов
/ 20 ноября 2011
<xsl:template match="@* | node()">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="content//*[not(self::SpecialContent)]">
  <xsl:apply-templates/>
</xsl:template>

должно хватить.

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