如何用XSLT将源XML作为文本值嵌入转换后的XML中?
用XSLT实现动态XML转换并保留原内容
完全可以通过XSLT实现你的需求,不管源XML的元素结构是否已知,都能动态处理并生成包含原内容文本副本的目标XML。以下是具体实现方案:
源XML示例
<root> <item> <a>Text&A</a> <b>Text&B</b> <c>Text&C</c> </item> <item> <a>TextA</a> <b>TextB</b> <c>TextC</c> </item> </root>
期望转换结果
<root> <item> <ab>Text&A Text&B</ab> <orig> <a>Text&amp;A</a> <b>Text&amp;B</b> <c>Text&amp;C</c> </orig> </item> <item> <ab>TextA TextB</ab> <orig> <a>TextA</a> <b>TextB</b> <c>TextC</c> </orig> </item> </root>
XSLT实现代码(XSLT 2.0+)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 匹配根节点,直接复制 --> <xsl:template match="/root"> <xsl:copy> <xsl:apply-templates select="item"/> </xsl:copy> </xsl:template> <!-- 处理每个item节点 --> <xsl:template match="item"> <xsl:copy> <!-- 生成ab元素,拼接a和b的文本内容 --> <ab> <xsl:value-of select="concat(a, ' ', b)"/> </ab> <!-- 生成orig元素,序列化当前item的所有子节点 --> <orig> <xsl:value-of select="serialize(*, map{'method':'xml', 'indent':true()})" disable-output-escaping="no"/> </orig> </xsl:copy> </xsl:template> <!-- 兜底模板:匹配未明确处理的节点,避免默认复制 --> <xsl:template match="text()|@*"/> </xsl:stylesheet>
关键说明
- 动态序列化原内容:使用XSLT 2.0的
serialize函数,可以将任意节点序列化为XML文本,自动处理转义(比如把<转成<,&转成&),完全不需要预知源节点的结构。 - 生成ab元素:直接通过
concat(a, ' ', b)拼接指定节点的文本,若后续需要调整,只需修改这部分逻辑即可。 - 兜底模板:避免默认的节点复制行为,确保只处理我们明确指定的内容。
兼容XSLT 1.0的方案(无serialize函数时)
如果只能用XSLT 1.0,可以通过递归模板实现节点的转义序列化:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/root"> <xsl:copy> <xsl:apply-templates select="item"/> </xsl:copy> </xsl:template> <xsl:template match="item"> <xsl:copy> <ab> <xsl:value-of select="concat(a, ' ', b)"/> </ab> <orig> <xsl:apply-templates select="*" mode="serialize"/> </orig> </xsl:copy> </xsl:template> <!-- 递归序列化节点的模板 --> <xsl:template match="*" mode="serialize"> <!-- 输出转义的起始标签 --> <xsl:text><</xsl:text> <xsl:value-of select="name()"/> <xsl:text>></xsl:text> <!-- 处理子节点:文本或嵌套元素 --> <xsl:apply-templates mode="serialize"/> <!-- 输出转义的结束标签 --> <xsl:text></</xsl:text> <xsl:value-of select="name()"/> <xsl:text>></xsl:text> <!-- 添加换行和缩进,模拟格式化 --> <xsl:text> </xsl:text> </xsl:template> <!-- 转义文本内容中的特殊字符 --> <xsl:template match="text()" mode="serialize"> <xsl:call-template name="escape-xml"> <xsl:with-param name="text" select="."/> </xsl:call-template> </xsl:template> <!-- 转义XML特殊字符的模板 --> <xsl:template name="escape-xml"> <xsl:param name="text"/> <xsl:choose> <xsl:when test="contains($text, '&')"> <xsl:value-of select="substring-before($text, '&')"/> <xsl:text>&amp;</xsl:text> <xsl:call-template name="escape-xml"> <xsl:with-param name="text" select="substring-after($text, '&')"/> </xsl:call-template> </xsl:when> <xsl:when test="contains($text, '<')"> <xsl:value-of select="substring-before($text, '<')"/> <xsl:text>&lt;</xsl:text> <xsl:call-template name="escape-xml"> <xsl:with-param name="text" select="substring-after($text, '<')"/> </xsl:call-template> </xsl:when> <xsl:when test="contains($text, '>')"> <xsl:value-of select="substring-before($text, '>')"/> <xsl:text>&gt;</xsl:text> <xsl:call-template name="escape-xml"> <xsl:with-param name="text" select="substring-after($text, '>')"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$text"/> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="text()|@*"/> </xsl:stylesheet>
这个XSLT 1.0版本通过递归处理每个节点和文本,手动完成XML特殊字符的转义,同样能实现动态保留原内容的需求。
内容的提问来源于stack exchange,提问作者xsrf
相关产品推荐
相关产品推荐

