我有以下平面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 - &gt; figure-caption-german - &gt; figure-caption-english),但是我不能排除它会被其他元素打断(这里是MISC-元素)。
我想将这三个元素包装在一个元素
中<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忍者技术”来完成所期望的任务。
任何帮助或指示都将受到高度赞赏。
致以最诚挚的问候,谢谢Matthias Einbrodt
答案 0 :(得分: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>
答案 1 :(得分: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 :复制这里或者替换)
在此模板中,您可以通过选择组中最后一个元素之后的节点来继续迭代
<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>
这种方法的一个优点是你可以抛出除英语和德语之外的其他语言,它应该仍然可以工作,语言的顺序也无关紧要。 (当然,你可能想忽略其他语言,在这种情况下它不起作用!)
答案 2 :(得分: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>
答案 3 :(得分:0)
基于JLR的解决方案,我受到启发,使用不同的模板模式实现了对问题的多遍解决方案。
鉴于以下平面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>