如何使用xsl根据属性将xml的兄弟节点变成父子节点
How to change the siblings of xml into parent and child nodes according to their attributes using xsl
输入是
<p style="abc">hbfg</p>
<r style="cds">bhf</r>
<r style="cds"> bhfsh</r>
<p style="abc">pofj</p>
<r style="abc"> bchs</r>
预期输出应该是
<p style="abc">hbfg
<r style="cds">bhf</r>
<r style="cds"> bhfsh</r></p>
<p style="abc">pofj
<r style="abc"> bchs</r></p>
如何使用 xslt 进行转换。
这里有两个版本的 XSLT 样式表,它们将处理 XML
您发布的文件,一个用于 xslt-2.0 的文件,它引入了一个方便的
xsl:for-each-group group-starting-with=pattern
这个元素
用例,并且为了获得最大的可移植性,一个用于 xslt-1.0 使用
XPath 做分组。两个版本都使用 doc/text
作为逻辑
树的根和 xsl:apply-templates
以充分利用
built-in 模板规则。注意空格处理。
平面文件转换的更多示例位于
SO
和 XSLT 1.0 FAQ,现在位于
archive.org.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="doc/text">
<chapter>
<title>
<xsl:apply-templates select="p[@style='TRH2']"/>
</title>
<research>
<title>
<xsl:apply-templates select="p[@style='TRRef']"/>
</title>
<reftext>
<xsl:apply-templates select="p[@style='TRRefText']"/>
</reftext>
</research>
<sections>
<xsl:for-each-group
select="p[not(@style) or @style='TRH7']"
group-starting-with="p[@style='TRH7']"
>
<title>
<xsl:apply-templates select="self::p[1]"/>
</title>
<paragraphs>
<xsl:for-each select="current-group()[self::p][position()>1]">
<para-text>
<xsl:apply-templates/>
</para-text>
</xsl:for-each>
</paragraphs>
</xsl:for-each-group>
</sections>
</chapter>
</xsl:template>
<xsl:template match="p[@style='TRRefText']">
<xsl:value-of select="."/><br/>
</xsl:template>
<xsl:template match="foot-note">
<footnoteref>
<id><xsl:value-of select="@id-rel"/></id>
<xsl:apply-templates/>
</footnoteref>
</xsl:template>
</xsl:transform>
XSLT 1.0 版本(在第三个 xsl:template
中)使用 XPath
用于对当前和之间的 non-title p
元素进行分组的表达式
下一个小节标题元素 (p[@style='TRH7']
),以及一个 mode="para"
避免将标题同时作为标题和段落处理的子句。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="doc/text">
<chapter>
<title>
<xsl:apply-templates select="p[@style='TRH2']" />
</title>
<research>
<title>
<xsl:apply-templates select="p[@style='TRRef']" />
</title>
<reftext>
<xsl:apply-templates select="p[@style='TRRefText'] "/>
</reftext>
</research>
<sections>
<xsl:apply-templates select="p[@style='TRH7']" />
</sections>
</chapter>
</xsl:template>
<xsl:template match="p[@style='TRRefText']">
<xsl:value-of select="."/><br/>
</xsl:template>
<xsl:template match="p[@style='TRH7']">
<title><xsl:apply-templates/></title>
<paragraphs>
<xsl:apply-templates mode="para"
select="following-sibling::p[not(@style='TRH7')]
[generate-id(preceding-sibling::p[@style='TRH7'][1])
= generate-id(current())]"
/>
</paragraphs>
</xsl:template>
<xsl:template match="p" mode="para">
<para-text><xsl:apply-templates/></para-text>
</xsl:template>
<xsl:template match="foot-note">
<footnoteref>
<id><xsl:value-of select="@id-rel"/></id>
<xsl:apply-templates/>
</footnoteref>
</xsl:template>
</xsl:transform>
更新:评论中要求的补充说明。
您自己的代码与我发布的代码非常接近,因此我将详细介绍如何使用 XSLT 1.0 对元素进行分组。文档中的每个 sub-section 都由其标题样式 (p[@style='TRH7']
) 触发,激活第三个模板:
<xsl:template match="p[@style='TRH7']">
<title><xsl:apply-templates/></title>
<paragraphs>
<xsl:apply-templates mode="para"
select="following-sibling::p[not(@style='TRH7')]
[generate-id(preceding-sibling::p[@style='TRH7'][1])
= generate-id(current())]"
/>
</paragraphs>
</xsl:template>
此模板发出一个 sub-section 标题(使用 built-in 模板规则),然后收集以下 non-title 段落
(following-sibling::p[not(@style='TRH7')]
) 有当前
标题作为最近的逻辑 parent。回想一下 preceding-sibling
是一个反向轴,因此 p[…][1]
指的是反向文档顺序中最近的兄弟。由于 following-sibling::p[…]
选择所有后续 non-title 段落,第二个谓词 [generate-id(…)]
将选择限制为当前标题的逻辑 children。
输入是
<p style="abc">hbfg</p>
<r style="cds">bhf</r>
<r style="cds"> bhfsh</r>
<p style="abc">pofj</p>
<r style="abc"> bchs</r>
预期输出应该是
<p style="abc">hbfg
<r style="cds">bhf</r>
<r style="cds"> bhfsh</r></p>
<p style="abc">pofj
<r style="abc"> bchs</r></p>
如何使用 xslt 进行转换。
这里有两个版本的 XSLT 样式表,它们将处理 XML
您发布的文件,一个用于 xslt-2.0 的文件,它引入了一个方便的
xsl:for-each-group group-starting-with=pattern
这个元素
用例,并且为了获得最大的可移植性,一个用于 xslt-1.0 使用
XPath 做分组。两个版本都使用 doc/text
作为逻辑
树的根和 xsl:apply-templates
以充分利用
built-in 模板规则。注意空格处理。
平面文件转换的更多示例位于 SO 和 XSLT 1.0 FAQ,现在位于 archive.org.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="doc/text">
<chapter>
<title>
<xsl:apply-templates select="p[@style='TRH2']"/>
</title>
<research>
<title>
<xsl:apply-templates select="p[@style='TRRef']"/>
</title>
<reftext>
<xsl:apply-templates select="p[@style='TRRefText']"/>
</reftext>
</research>
<sections>
<xsl:for-each-group
select="p[not(@style) or @style='TRH7']"
group-starting-with="p[@style='TRH7']"
>
<title>
<xsl:apply-templates select="self::p[1]"/>
</title>
<paragraphs>
<xsl:for-each select="current-group()[self::p][position()>1]">
<para-text>
<xsl:apply-templates/>
</para-text>
</xsl:for-each>
</paragraphs>
</xsl:for-each-group>
</sections>
</chapter>
</xsl:template>
<xsl:template match="p[@style='TRRefText']">
<xsl:value-of select="."/><br/>
</xsl:template>
<xsl:template match="foot-note">
<footnoteref>
<id><xsl:value-of select="@id-rel"/></id>
<xsl:apply-templates/>
</footnoteref>
</xsl:template>
</xsl:transform>
XSLT 1.0 版本(在第三个 xsl:template
中)使用 XPath
用于对当前和之间的 non-title p
元素进行分组的表达式
下一个小节标题元素 (p[@style='TRH7']
),以及一个 mode="para"
避免将标题同时作为标题和段落处理的子句。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="doc/text">
<chapter>
<title>
<xsl:apply-templates select="p[@style='TRH2']" />
</title>
<research>
<title>
<xsl:apply-templates select="p[@style='TRRef']" />
</title>
<reftext>
<xsl:apply-templates select="p[@style='TRRefText'] "/>
</reftext>
</research>
<sections>
<xsl:apply-templates select="p[@style='TRH7']" />
</sections>
</chapter>
</xsl:template>
<xsl:template match="p[@style='TRRefText']">
<xsl:value-of select="."/><br/>
</xsl:template>
<xsl:template match="p[@style='TRH7']">
<title><xsl:apply-templates/></title>
<paragraphs>
<xsl:apply-templates mode="para"
select="following-sibling::p[not(@style='TRH7')]
[generate-id(preceding-sibling::p[@style='TRH7'][1])
= generate-id(current())]"
/>
</paragraphs>
</xsl:template>
<xsl:template match="p" mode="para">
<para-text><xsl:apply-templates/></para-text>
</xsl:template>
<xsl:template match="foot-note">
<footnoteref>
<id><xsl:value-of select="@id-rel"/></id>
<xsl:apply-templates/>
</footnoteref>
</xsl:template>
</xsl:transform>
更新:评论中要求的补充说明。
您自己的代码与我发布的代码非常接近,因此我将详细介绍如何使用 XSLT 1.0 对元素进行分组。文档中的每个 sub-section 都由其标题样式 (p[@style='TRH7']
) 触发,激活第三个模板:
<xsl:template match="p[@style='TRH7']">
<title><xsl:apply-templates/></title>
<paragraphs>
<xsl:apply-templates mode="para"
select="following-sibling::p[not(@style='TRH7')]
[generate-id(preceding-sibling::p[@style='TRH7'][1])
= generate-id(current())]"
/>
</paragraphs>
</xsl:template>
此模板发出一个 sub-section 标题(使用 built-in 模板规则),然后收集以下 non-title 段落
(following-sibling::p[not(@style='TRH7')]
) 有当前
标题作为最近的逻辑 parent。回想一下 preceding-sibling
是一个反向轴,因此 p[…][1]
指的是反向文档顺序中最近的兄弟。由于 following-sibling::p[…]
选择所有后续 non-title 段落,第二个谓词 [generate-id(…)]
将选择限制为当前标题的逻辑 children。