我想用多个元素替换XML(大(中的元素,如下所示:
原始 XML:
<root>
<cr>
<id>1</id>
<release>A</release>
</cr>
<cr>
<id>2</id>
<release>B</release>
</cr>
</root>
我希望输出是:
<root>
<cr>
<id>1</id>
<release>Aa</release>
<release>Ab</release>
<release>Ad</release>
</cr>
<cr>
<id>2</id>
<release>Bd</release>
<release>Be</release>
</cr>
</root>
原则是,每当有//release[text((='A'] 时,将元素替换为上面的三个,每当有//release[text((='B'] 时,将元素替换为上面的两个,依此类推。如果发布文本((是"C"或"D"或其他值,则它们保持相同的值。
我的尝试:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//cr/release/text()">
<xsl:if test=".='A'">
<xsl:value-of select="Aa"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
它适用于一个 -> 一个替换,但如何做多个?多谢
这是一个完全通用且简短的 XSLT 1.0 解决方案。它使用映射 xml 文件,该文件指定每个所需版本的替换:
(请在此答案的末尾找到用 XSLT 2.0 编写的最终通用和简短的解决方案。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vMap" select="document('mapping.xml')/*/*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="release[. = document('mapping.xml')/*/release/@old]">
<xsl:copy-of select="$vMap[@old = current()]/*"/>
</xsl:template>
</xsl:stylesheet>
如果文件mapping.xml
与转换(.xsl 文件(位于同一目录中,并且:
<map>
<release old="A">
<release>Aa</release>
<release>Ab</release>
<release>Ad</release>
</release>
<release old="B">
<release>Ba</release>
<release>Bb</release>
<release>Bd</release>
</release>
</map>
然后,当转换应用于此 XML 文档时:
<root>
<cr>
<id>1</id>
<release>A</release>
</cr>
<cr>
<id>2</id>
<release>B</release>
</cr>
<cr>
<id>3</id>
<release>C</release>
</cr>
<cr>
<id>4</id>
<release>D</release>
</cr>
</root>
生成所需的正确结果:
<root>
<cr>
<id>1</id>
<release>Aa</release>
<release>Ab</release>
<release>Ad</release>
</cr>
<cr>
<id>2</id>
<release>Ba</release>
<release>Bb</release>
<release>Bd</release>
</cr>
<cr>
<id>3</id>
<release>C</release>
</cr>
<cr>
<id>4</id>
<release>D</release>
</cr>
</root>
注: 许多不同名称的元素可能会替换为相同的转换。
如果我们有这个映射文件:
<map>
<release old="A">
<release>Aa</release>
<release>Ab</release>
<release>Ad</release>
</release>
<release old="B">
<release>Ba</release>
<release>Bb</release>
<release>Bd</release>
</release>
<history old="p">
<history>Pp</history>
<history>Pq</history>
<history>Pr</history>
</history>
</map>
和这个源 XML 文档:
<root>
<cr>
<id>1</id>
<history>p</history>
<release>A</release>
</cr>
<cr>
<id>2</id>
<history>q</history>
<release>B</release>
</cr>
<cr>
<id>3</id>
<history>r</history>
<release>C</release>
</cr>
<cr>
<id>4</id>
<history>t</history>
<release>D</release>
</cr>
</root>
那么这个转换:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vMap" select="document('mapping.xml')/*/*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="release[. = document('mapping.xml')/*/release/@old]">
<xsl:copy-of select="$vMap[@old = current()]/*"/>
</xsl:template>
<xsl:template match="history[. = document('mapping.xml')/*/history/@old]">
<xsl:copy-of select="$vMap[@old = current()]/*"/>
</xsl:template>
</xsl:stylesheet>
当应用于上述 XML 文档时,会产生所需的结果 -- 其中映射和替换多个不同名称的元素:
<root>
<cr>
<id>1</id>
<history>Pp</history>
<history>Pq</history>
<history>Pr</history>
<release>Aa</release>
<release>Ab</release>
<release>Ad</release>
</cr>
<cr>
<id>2</id>
<history>q</history>
<release>Ba</release>
<release>Bb</release>
<release>Bd</release>
</cr>
<cr>
<id>3</id>
<history>r</history>
<release>C</release>
</cr>
<cr>
<id>4</id>
<history>t</history>
<release>D</release>
</cr>
</root>
最后:可以在 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:variable name="vMap" select="document('mapping.xml')/*/*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"*[. = $vMap[name() eq name(current())]/@old]">
<xsl:copy-of select="$vMap[name() eq name(current()) and @old eq current()]/*"/>
</xsl:template>
</xsl:stylesheet>
请注意:在此转换中没有硬编码的元素名称!
更大的优势是:我们可以修改转换,因此我们可以在调用转换时将映射文档的 URI 作为全局参数传递 - 因此我们可以有一个单一的通用转换,该转换适用于任何未知的预先映射。
只有这些是必需的更改:
<xsl:param name="pmapUrl" select="'file:///c:/temp/mapping.xml'"/>
<xsl:variable name="vMap" select="document($pmapUrl)/*/*"/>
完整的转换(全局参数pmapUrl
可以并且通常将由转换的调用者动态指定(:
<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="pmapUrl" select="'file:///c:/temp/mapping.xml'"/>
<xsl:variable name="vMap" select="document($pmapUrl)/*/*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"*[for $name in name()
return
. = $vMap[name() eq $name]/@old]">
<xsl:copy-of select="$vMap[@old = current()]/*"/>
</xsl:template>
</xsl:stylesheet>
试试这个方式:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="release[.='A']">
<release>Aa</release>
<release>Ab</release>
<release>Ad</release>
</xsl:template>
<xsl:template match="release[.='B']">
<release>Bd</release>
<release>Be</release>
</xsl:template>
</xsl:stylesheet>
测试输入
<root>
<cr>
<id>1</id>
<release>A</release>
</cr>
<cr>
<id>2</id>
<release>B</release>
</cr>
<cr>
<id>3</id>
<release>C</release>
</cr>
</root>
结果
<?xml version="1.0" encoding="UTF-8"?>
<root>
<cr>
<id>1</id>
<release>Aa</release>
<release>Ab</release>
<release>Ad</release>
</cr>
<cr>
<id>2</id>
<release>Bd</release>
<release>Be</release>
</cr>
<cr>
<id>3</id>
<release>C</release>
</cr>
</root>