打破XSLT输出中的流量

时间:2011-11-20 16:00:04

标签: xml xslt

如何制作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 个答案:

答案 0 :(得分:2)

目前所有现有答案似乎都在这个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>

答案 1 :(得分:0)

<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>

应该足够了。

答案 2 :(得分:0)

<?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>

不是很漂亮,但如果有的话,应该使用其他转换

答案 3 :(得分:0)

我认为您可以实现此目的的一种方法是将所有元素*元素按第一个**特殊元素分组,该元素是后代或后续元素,

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

然后,您可以通过匹配文档

中的所有 speical 元素开始
 <xsl:apply-templates select="//special" />

对于每个匹配的特殊元素,您可以将所有关联的元素元素组合在一起,这些元素将此当前元素作为其第一个后代或后续元素。< / p>

<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以删除它。)

答案 4 :(得分:0)

Dimitri的回答非常好,但由于生成的内容和非常深的模板层次结构,它会破坏我的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" />

任何关于如何改进的建议都会很乐意。