英文:
How to perform multiple, sequential transformations in one XSLT stylesheet
问题
我必须对XML文档执行两个转换操作,首先需要修改它,然后需要对先前修改的结果进行排序。
XML文档如下所示:
<root>
<s>
<a>
<b>
<c>
<f1>1</f1>
<f2>0004</f2>
</c>
<c>
<f1>1</f1>
<f2>003x</f2>
</c>
<c>
<f1>0</f1>
<f2>0002</f2>
</c>
<c>
<f1>1</f1>
<f2>0001</f2>
</c>
</b>
</a>
</s>
<s>
<a>
<b>
<c>
<f1>0</f1>
<f2>0006</f2>
</c>
<c>
<f1>0</f1>
<f2>005x</f2>
</c>
</b>
</a>
</s>
</root>
我有两个XSLT样式表,一个用于每个转换,如果分别执行它们,它们可以正常工作,即先应用第一个,然后应用第二个到第一个的结果上。
这是第一个样式表(值修改转换):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" omit-xml-declaration="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//f2">
<xsl:copy>
<xsl:choose>
<xsl:when test="ends-with(.,'x')">
<xsl:value-of select="concat('0', .)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
这是第二个样式表(子结构排序):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/s/a/b">
<xsl:copy>
<xsl:apply-templates>
<xsl:sort select="f1"/>
<xsl:sort select="f2"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
这两个样式表应用于XML文档的结果如下:
<root>
<s>
<a>
<b>
<c>
<f1>0</f1>
<f2>0002</f2>
</c>
<c>
<f1>1</f1>
<f2>0001</f2>
</c>
<c>
<f1>1</f1>
<f2>0003x</f2>
</c>
<c>
<f1>1</f1>
<f2>0004</f2>
</c>
</b>
</a>
</s>
<s>
<a>
<b>
<c>
<f1>0</f1>
<f2>0005x</f2>
</c>
<c>
<f1>0</f1>
<f2>0006</f2>
</c>
</b>
</a>
</s>
</root>
现在,我需要将它们合并成一个XSLT样式表,以便在一步中执行,但是到目前为止我的尝试无法获得相同的结果,第二个转换应用于原始XML文档而不是第一个转换的结果。
这是我正在尝试的XSLT样式表:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" omit-xml-declaration="yes"/>
<!-- 开始第一个转换 -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//f2">
<xsl:copy>
<xsl:choose>
<xsl:when test="ends-with(.,'x')">
<xsl:value-of select="concat('0', .)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
<!-- 结束第一个转换 -->
<!-- 开始第二个转换 -->
<xsl:template match="root/s/a/b">
<xsl:copy>
<xsl:apply-templates>
<xsl:sort select="f1"/>
<xsl:sort select="f2"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<!-- 结束第二个转换 -->
</xsl:stylesheet>
这个合并样式表的结果如下:
<root>
<s>
<a>
<b>
<c>
<f1>0</f1>
<f2>0002</f2>
</c>
<c>
<f1>1</f1>
<f2>0001</
<details>
<summary>英文:</summary>
I have to perform two transformations over an XML document, first I need to modify it and then I need to sort the result of the previous modification.
The XML document is as follows:
```lang-xml
<root>
<s>
<a>
<b>
<c>
<f1>1</f1>
<f2>0004</f2>
</c>
<c>
<f1>1</f1>
<f2>003x</f2>
</c>
<c>
<f1>0</f1>
<f2>0002</f2>
</c>
<c>
<f1>1</f1>
<f2>0001</f2>
</c>
</b>
</a>
</s>
<s>
<a>
<b>
<c>
<f1>0</f1>
<f2>0006</f2>
</c>
<c>
<f1>0</f1>
<f2>005x</f2>
</c>
</b>
</a>
</s>
</root>
I have two XSLT stylesheets, one for each transformation, they work well if executed separately, I mean in two steps, apply the first one and then apply the second one over the result of the first one.
This is the first stylesheet (value modification transformation):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" omit-xml-declaration="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//f2">
<xsl:copy>
<xsl:choose>
<xsl:when test="ends-with(.,'x')">
<xsl:value-of select="concat('0', .)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This other is the second one (substructure sorting):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/s/a/b">
<xsl:copy>
<xsl:apply-templates>
<xsl:sort select="f1"/>
<xsl:sort select="f2"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The result of these two stylesheets applied to the XML document is:
<root>
<s>
<a>
<b>
<c>
<f1>0</f1>
<f2>0002</f2>
</c>
<c>
<f1>1</f1>
<f2>0001</f2>
</c>
<c>
<f1>1</f1>
<f2>0003x</f2>
</c>
<c>
<f1>1</f1>
<f2>0004</f2>
</c>
</b>
</a>
</s>
<s>
<a>
<b>
<c>
<f1>0</f1>
<f2>0005x</f2>
</c>
<c>
<f1>0</f1>
<f2>0006</f2>
</c>
</b>
</a>
</s>
</root>
This is the expected result and it is correct.
Now I need to merge them into only one XSLT stylesheet, to be executed in only one step, but my attempts so far can't get the same result, the second transformation is applied to the original XML document instead of the result of the first transformation.
This is XSLT stylesheet I'm trying:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" omit-xml-declaration="yes"/>
<!-- Begin first transformation -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//f2">
<xsl:copy>
<xsl:choose>
<xsl:when test="ends-with(.,'x')">
<xsl:value-of select="concat('0', .)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
<!-- End first transformation -->
<!-- Begin second transformation -->
<xsl:template match="root/s/a/b">
<xsl:copy>
<xsl:apply-templates>
<xsl:sort select="f1"/>
<xsl:sort select="f2"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<!-- End second transformation -->
</xsl:stylesheet>
The result of this attempt on merged stylesheet is:
<root>
<s>
<a>
<b>
<c>
<f1>0</f1>
<f2>0002</f2>
</c>
<c>
<f1>1</f1>
<f2>0001</f2>
</c>
<c>
<f1>1</f1>
<f2>0004</f2>
</c>
<c>
<f1>1</f1>
<f2>0003x</f2>
</c>
</b>
</a>
</s>
<s>
<a>
<b>
<c>
<f1>0</f1>
<f2>0006</f2>
</c>
<c>
<f1>0</f1>
<f2>0005x</f2>
</c>
</b>
</a>
</s>
</root>
The problem with this result is that the 'c' substructures which values of field f2 ending with 'x' are in the wrong positions.
Even if I try another copy template like the first one just before the sorting template the result is still not the desired. I don't know what I'm doing wrong, please help me.
Sorry if this question is too long, I tried to make shorter but it lead to misunderstandings.
Thanks.
答案1
得分: 0
你可以简单地将这两个转换组合如下:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/root">
<xsl:variable name="first-transformation">
<xsl:apply-templates mode="first"/>
</xsl:variable>
<xsl:copy>
<xsl:apply-templates select="$first-transformation"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*|node()" mode="#all">
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="f2[ends-with(., 'x')]" mode="first">
<xsl:copy>
<xsl:value-of select="concat('0', .)"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b">
<xsl:copy>
<xsl:apply-templates select="c">
<xsl:sort select="f1"/>
<xsl:sort select="f2"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
这是 XSLT 2.0 的代码。
英文:
You could combine the 2 transforms simply as:
(edited)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/root">
<xsl:variable name="first-transformation">
<xsl:apply-templates mode="first"/>
</xsl:variable>
<xsl:copy>
<xsl:apply-templates select="$first-transformation"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*|node()" mode="#all">
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="f2[ends-with(., 'x')]" mode="first">
<xsl:copy>
<xsl:value-of select="concat('0', .)"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b">
<xsl:copy>
<xsl:apply-templates select="c">
<xsl:sort select="f1"/>
<xsl:sort select="f2"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This is in XSLT 2.0.
答案2
得分: 0
使用XSLT 3.0,如果您想要链接一系列单独的样式表,您可以使用fold-left
和transform
,例如:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all">
<xsl:param name="stylesheet-uris" as="xs:string*" select="'sheet1.xsl', 'sheet2.xsl'"/>
<xsl:template name="xsl:initial-template" match="/">
<xsl:sequence
select="fold-left($stylesheet-uris, ., function($node, $xslt) {
transform(
map {
'stylesheet-location' : $xslt,
'source-node' : $node
}
)?output
})"/>
</xsl:template>
</xsl:stylesheet>
英文:
Using XSLT 3.0, if you want to chain a sequence of separate stylesheets, you can do that using fold-left
and transform
e.g.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all">
<xsl:param name="stylesheet-uris" as="xs:string*" select="'sheet1.xsl', 'sheet2.xsl'"/>
<xsl:template name="xsl:initial-template" match="/">
<xsl:sequence
select="fold-left($stylesheet-uris, ., function($node, $xslt) {
transform(
map {
'stylesheet-location' : $xslt,
'source-node' : $node
}
)?output
})"/>
</xsl:template>
</xsl:stylesheet>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论