2

我有以下平面 XML 结构

<div class="section-level-1">

  <!-- other elements -->

  <p class="para">
    <img src="..." alt="..." title="..." />
  </p>
  <p class="figure-caption-german">
    <img src="..." alt="..." title="..." />
  </p>
  <p class="figure-caption-english">
    <img src="..." alt="..." title="..." />
  </p>

  <!-- other elements -->

  <p class="para">
    <img src="..." alt="..." title="..." />
  </p>
  <p class="figure-caption-german">
    <img src="..." alt="..." title="..." />
  </p>
  <misc-element>...</misc-element>
  <p class="figure-caption-english">
    <img src="..." alt="..." title="..." />
  </p>
</div>

这些元素的顺序总是相同的(para -> figure-caption-german -> figure-caption-english),但是我不能排除它会被其他元素(这里是 misc-element)打断。

我想将这三个元素包装在一个元素中

<div class="section-level-1">

  <!-- other elements -->

  <div class="figure">
    <p class="para">
      <img src="..." alt="..." title="..." />
    </p>
    <p class="figure-caption-german">
      <img src="..." alt="..." title="..." />
    </p>
    <p class="figure-caption-english">
      <img src="..." alt="..." title="..." />
    </p>
  </div>

  <!-- other elements -->

  <div class="figure">
    <p class="para">
      <img src="..." alt="..." title="..." />
    </p>
    <p class="figure-caption-german">
      <img src="..." alt="..." title="..." />
    </p>
    <p class="figure-caption-english">
      <img src="..." alt="..." title="..." />
    </p>
  </div>
</div>

中断元素不需要保留,可以删除。

到目前为止我所拥有的

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

<!-- Html Ninja Pattern -->

<xsl:template match="*">
  <xsl:element name="{name()}">
    <xsl:apply-templates select="* | @* | text()"/>
  </xsl:element>
</xsl:template>

<xsl:template match="body//@*">
  <xsl:attribute name="{name(.)}">
    <xsl:value-of select="."/>
  </xsl:attribute>
</xsl:template>

<!-- Modify certain elements -->

<xsl:template match="" priority="1">
  <!-- do something -->
</xsl:template>

作为一个基本模式,我借鉴了“Html Ninja Technique”(http://getsymphony.com/learn/articles/view/html-ninja-technique/),因为它允许我只处理那些我需要转换的特定元素,而将所有其他元素不变地发送到输出树。到目前为止,一切正常,但现在我似乎真的遇到了障碍。我什至不确定是否可以依靠“Html Ninja Technique”来完成所需的任务。

任何帮助或指示将不胜感激。

最好的问候,谢谢你,马蒂亚斯·艾因布罗德

4

4 回答 4

0

这有点涉及,但我认为应该这样做:

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

  <xsl:template match="*" name="Copy">
    <xsl:element name="{name()}">
      <xsl:apply-templates select="* | @* | text()"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="@*">
    <xsl:attribute name="{name(.)}">
      <xsl:value-of select="."/>
    </xsl:attribute>
  </xsl:template>

  <xsl:template match="div[starts-with(@class, 'section-level')]">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <!-- Apply templates to paras and anything with no preceding sibling
           or with a figure-caption-english preceding sibling-->
      <xsl:apply-templates select="p[@class = 'para'] | 
                                 *[not(preceding-sibling::*) or
                                    preceding-sibling::*[1][self::p]
                                      [@class = 'figure-caption-english']
                                  ]"
                           mode="iter"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="p[@class = 'para']" mode="iter">
    <div class="figure">
      <xsl:call-template name="Copy" />
      <!-- Apply templates to the next english and german figure captions -->
      <xsl:apply-templates
        select="following-sibling::p[@class = 'figure-caption-german'][1] |
                following-sibling::p[@class = 'figure-caption-english'][1]" />
    </div>
  </xsl:template>

  <xsl:template match="*" mode="iter">
    <xsl:call-template name="Copy" />
    <xsl:apply-templates 
        select="following-sibling::*[1]
                      [not(self::p[@class = 'para'])]"
        mode="iter"/>
  </xsl:template>
</xsl:stylesheet>

应用于此示例数据时:

<div class="section-level-1">

  <!-- other elements -->
  <div>hello</div>
  <div>hello</div>
  <div>hello</div>
  <div>hello</div>
  <p class="para">
    <img src="..." alt="..." title="..." />
  </p>
  <p class="figure-caption-german">
    <img src="..." alt="..." title="..." />
  </p>
  <p class="figure-caption-english">
    <img src="..." alt="..." title="..." />
  </p>

  <!-- other elements -->
  <div>hello</div>
  <div>hello</div>
  <div>hello</div>

  <p class="para">
    <img src="..." alt="..." title="..." />
  </p>
  <p class="figure-caption-german">
    <img src="..." alt="..." title="..." />
  </p>
  <misc-element>...</misc-element>
  <p class="figure-caption-english">
    <img src="..." alt="..." title="..." />
  </p>
  <div>hello</div>
  <div>hello</div>
  <div>hello</div>
</div>

它产生:

<div class="section-level-1">
  <div>hello</div>
  <div>hello</div>
  <div>hello</div>
  <div>hello</div>
  <div class="figure">
    <p class="para">
      <img src="..." alt="..." title="..." />
    </p>
    <p class="figure-caption-german">
      <img src="..." alt="..." title="..." />
    </p>
    <p class="figure-caption-english">
      <img src="..." alt="..." title="..." />
    </p>
  </div>
  <div>hello</div>
  <div>hello</div>
  <div>hello</div>
  <div class="figure">
    <p class="para">
      <img src="..." alt="..." title="..." />
    </p>
    <p class="figure-caption-german">
      <img src="..." alt="..." title="..." />
    </p>
    <p class="figure-caption-english">
      <img src="..." alt="..." title="..." />
    </p>
  </div>
  <div>hello</div>
  <div>hello</div>
  <div>hello</div>
</div>
于 2013-03-12T00:32:42.810 回答
0

这是另一种方法。这确实涉及迭代div的子元素,但也使用xsl:key对相关的p元素进行分组。

首先,定义一个键来按第一个最前面的“para”元素对“figure-caption”元素进行分组:

<xsl:key name="para" 
     match="p[starts-with(@class, 'figure-caption')]" 
     use="generate-id(preceding-sibling::p[@class='para'][1])"/>

然后,首先匹配div元素,然后选择第一个元素

<xsl:template match="div">
   <div>
      <xsl:apply-templates select="node()[1]" mode="iterate"/>
   </div>
</xsl:template>

模式迭代用于指示将递归匹配其后续兄弟的模板。您首先需要一个模板来匹配“para”元素,您可以在其中使用键对相关元素进行分组

<xsl:template match="p[@class='para']" mode="iterate">
   <div class="figure">
      <xsl:apply-templates select=".|key('para', generate-id())" mode="group"/>
   </div>

(此处的模式将用于指示对于分组的元素,匹配模板将仅输出它们,但不会在下一个兄弟处进行处理。您可以在这里使用xsl:copy-of替代)

然后在此模板中,通过选择组中最后一个元素之后的节点来进行迭代

<xsl:apply-templates 
     select="key('para', generate-id())[last()]/following-sibling::node()[1]" mode="iterate"/>

然后可以将迭代中的其他元素与更通用的模板匹配以复制它们,并在下一个兄弟元素处继续

<xsl:template match="node()" mode="iterate">
   <xsl:call-template name="identity"/>
   <xsl:apply-templates select="following-sibling::node()[1]" mode="iterate"/>
</xsl:template>

这里的身份将调用身份模板。

这是完整的 XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:key name="para" match="p[starts-with(@class, 'figure-caption')]" use="generate-id(preceding-sibling::p[@class='para'][1])"/>

   <xsl:template match="div">
      <div>
         <xsl:copy-of select="@*"/>
         <xsl:apply-templates select="node()[1]" mode="iterate"/>
      </div>
   </xsl:template>

   <xsl:template match="p[@class='para']" mode="iterate">
      <div class="figure">
         <xsl:apply-templates select=".|key('para', generate-id())" mode="group"/>
      </div>
      <xsl:apply-templates select="key('para', generate-id())[last()]/following-sibling::node()[1]" mode="iterate"/>
   </xsl:template>

   <xsl:template match="node()" mode="group">
      <xsl:call-template name="identity"/>
   </xsl:template>

   <xsl:template match="node()" mode="iterate">
      <xsl:call-template name="identity"/>
      <xsl:apply-templates select="following-sibling::node()[1]" mode="iterate"/>
   </xsl:template>

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

应用于您的示例 XML 时,将输出以下内容

<div class="section-level-1">
   <!-- other elements -->
   <div class="figure">
      <p class="para">
         <img src="..." alt="..." title="..."/>
      </p>
      <p class="figure-caption-german">
         <img src="..." alt="..." title="..."/>
      </p>
      <p class="figure-caption-english">
         <img src="..." alt="..." title="..."/>
      </p>
   </div>
   <!-- other elements -->
   <div class="figure">
      <p class="para">
         <img src="..." alt="..." title="..."/>
      </p>
      <p class="figure-caption-german">
         <img src="..." alt="..." title="..."/>
      </p>
      <p class="figure-caption-english">
         <img src="..." alt="..." title="..."/>
      </p>
   </div>
</div>

这种方法的一个优点是您可以将除英语和德语之外的其他语言混合使用,它应该仍然可以工作,并且语言的顺序也无关紧要。(当然,你可能想忽略其他语言,在这种情况下它就行不通了!)

于 2013-03-12T22:53:26.457 回答
0

一个简单的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:strip-space elements="*"/>
 <xsl:param name="pClasses" select=
 "'para', 'figure-caption-german', 'figure-caption-english'"/>

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

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

   <xsl:for-each-group select="p[@class=$pClasses]"
     group-starting-with="p[@class eq $pClasses[1]]">
     <div class="figure">
       <xsl:apply-templates select="current-group()"/>
     </div>
    </xsl:for-each-group>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时:

<div class="section-level-1">

  <!-- other elements -->

  <p class="para">
    <img src="..." alt="..." title="..." />
  </p>
  <p class="figure-caption-german">
    <img src="..." alt="..." title="..." />
  </p>
  <p class="figure-caption-english">
    <img src="..." alt="..." title="..." />
  </p>

  <!-- other elements -->

  <p class="para">
    <img src="..." alt="..." title="..." />
  </p>
  <p class="figure-caption-german">
    <img src="..." alt="..." title="..." />
  </p>
  <misc-element>...</misc-element>
  <p class="figure-caption-english">
    <img src="..." alt="..." title="..." />
  </p>
</div>

产生了想要的正确结果:

<div class="section-level-1">
   <div class="figure">
      <p class="para">
         <img src="..." alt="..." title="..."/>
      </p>
      <p class="figure-caption-german">
         <img src="..." alt="..." title="..."/>
      </p>
      <p class="figure-caption-english">
         <img src="..." alt="..." title="..."/>
      </p>
   </div>
   <div class="figure">
      <p class="para">
         <img src="..." alt="..." title="..."/>
      </p>
      <p class="figure-caption-german">
         <img src="..." alt="..." title="..."/>
      </p>
      <p class="figure-caption-english">
         <img src="..." alt="..." title="..."/>
      </p>
   </div>
</div>
于 2013-03-13T04:15:56.580 回答
0

基于 JLRishe 的解决方案,我受到启发,使用不同的模板模式来实现该问题的多通道解决方案。

给定以下平面 XML 结构

<div class="section-level-1">

  <!-- other elements -->

  <p class="para">
    <img src="..." alt="..." title="..." />
  </p>
  <p class="figure-caption-german">
    <img src="..." alt="..." title="..." />
  </p>
  <p class="figure-caption-english">
    <img src="..." alt="..." title="..." />
  </p>

  <!-- other elements -->

  <p class="para">
    <img src="..." alt="..." title="..." />
  </p>
  <p class="figure-caption-german">
    <img src="..." alt="..." title="..." />
  </p>
  <misc-element>...</misc-element>
  <p class="figure-caption-english">
    <img src="..." alt="..." title="..." />
  </p>
</div>

我应用了以下方法。

<xsl:template match="/">
  <xsl:variable name="pass0">
    <xsl:apply-templates mode="pass0" />
  </xsl:variable>

  <xsl:variable name="pass1">
    <xsl:for-each select="$pass0">
      <xsl:apply-templates mode="pass1" />
    </xsl:for-each>
  </xsl:variable>

  <xsl:copy-of select="$pass1" />        
</xsl:template>

<!--###############
    ### Pass 0 #### 
    ###############-->

<xsl:template match="*" mode="pass0">
  <xsl:element name="{name()}">
    <xsl:apply-templates select="* | @* | text()" mode="pass0"/>
  </xsl:element>
</xsl:template>

<xsl:template match="@*" mode="pass0">
  <xsl:attribute name="{name(.)}">
    <xsl:value-of select="."/>
  </xsl:attribute>
</xsl:template>

<!-- wraps figures and their associated captions within <div class="figure"> element -->
<xsl:template match="p[@class = 'para'][img]" mode="pass0" priority="1">
  <div class="figure">
    <xsl:copy-of select="./img" />
      <xsl:apply-templates 
        select="following-sibling::p[@class = 'figure-caption-german'][1] |
                following-sibling::p[@class = 'figure-caption-english'][1]" 
        mode="fig- captions-pass0" />
  </div>
</xsl:template>

<xsl:template match="*" mode="fig-captions-pass0" priority="1">
  <xsl:copy-of select="." />
</xsl:template>

<!--###############
    ### Pass 1 #### 
    ###############-->

<xsl:template match="*" mode="pass1">
  <xsl:element name="{name()}">
    <xsl:apply-templates select="* | @* | text()" mode="pass1"/>
  </xsl:element>
</xsl:template>

<xsl:template match="@*" mode="pass1">
  <xsl:attribute name="{name(.)}">
    <xsl:value-of select="."/>
  </xsl:attribute>
</xsl:template>

<!-- removes all elements with figure captions that don't reside within <div class="figure"> element and all other unnecessary elements -->
<xsl:template match="
  p[@class = 'figure-caption-german'][not(parent::div[@class = 'figure'])] |
  p[@class = 'figure-caption-english'][not(parent::div[@class = 'figure'])] |
  misc-element" 
  mode="pass1" priority="1" />

结果我得到了想要的输出

<div class="section-level-1">

  <p class="para">normal paragraph etc.</p>
  <p class="para">normal paragraph etc.</p>
  <p class="para">normal paragraph etc.</p>

  <div class="figure">
    <img src="..." alt="..." title="..."></img>
    <p class="figure-caption-german">
      figure caption in german
    </p>
    <p class="figure-caption-english">
      figure caption in english
    </p>
  </div>

  <p class="para">normal paragraph etc.</p>
  <p class="para">normal paragraph etc.</p>
  <p class="para">normal paragraph etc.</p>

  <div class="figure">
    <img src="..." alt="..." title="..."></img>
    <p class="figure-caption-german">
      figure caption in german
    </p>
    <p class="figure-caption-english">
      figure caption in english 
    </p>
  </div>
</div>
于 2013-03-13T10:39:55.643 回答