XSLT转换中CDATA标签丢失问题求助
问题描述
- 输入XML:
<btask> <instruction>client_save</instruction> <section_data> <client> <mode_low>12200</mode_low> <swift/> <wcn/> <okved_version>1</okved_version> <add_info><![CDATA[<msg><kig>781/4</kig></msg>]]> </add_info> </client> </section_data> </btask>
- 期望输出XML:
<btask> <instruction code="client_save"/> <section_data> <field code="mode_low">12200</field> <field code="swift"/> <field code="wcn"/> <field code="okved_version">1</field> <field code="add_info"><![CDATA[<msg><kig>781/4</kig></msg>]]></field> </section_data> </btask>
- 现有问题:转换后
add_info的CDATA标签丢失,内容被转义,得到:
<field code="add_info"><msg><kig>781/4</kig></msg></field>
- 限制条件:不能使用
disable-output-escaping方案。 - 现有XSLT代码:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:param name="group_fields"/> <xsl:template match="/"> <xsl:element name="btask"> <xsl:for-each select="//btask/*[name()!='section_data' and name()!='section_data_tech' and name()!='instruction']"> <xsl:attribute name="{name()}"> <xsl:value-of select="./text()"/> </xsl:attribute> </xsl:for-each> <xsl:if test="count(//btask/instruction) = 1"> <xsl:element name="instruction"> <xsl:attribute name="code"> <xsl:value-of select="//btask/instruction"/> </xsl:attribute> </xsl:element> </xsl:if> <xsl:for-each select="//btask/*[name()='section_data' or name()='section_data_tech']"> <xsl:call-template name="sectiondata"> <xsl:with-param name="e" select="."/> </xsl:call-template> </xsl:for-each> </xsl:element> </xsl:template> <xsl:template name="sectiondata"> <xsl:param name="e"/> <xsl:copy> <xsl:for-each select="$e/*"> <xsl:call-template name="entity"> <xsl:with-param name="e" select="."/> </xsl:call-template> </xsl:for-each> </xsl:copy> </xsl:template> <xsl:template name="entity"> <xsl:param name="e"/> <xsl:element name="entity_data"> <xsl:attribute name="code"> <xsl:choose> <xsl:when test="string-length($e/@code)"> <xsl:value-of select="$e/@code"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="name()"/> </xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:for-each select="$e/*"> <xsl:choose> <xsl:when test="count(./*)>0 or $group_fields=name()"> <!-- groupdata or entitydata--> <xsl:choose> <xsl:when test="count(./*/*)>0 or $group_fields=name()"> <!-- groupdata--> <xsl:element name="group_data"> <xsl:attribute name="code"> <xsl:value-of select="name()"/> </xsl:attribute> <xsl:for-each select="./*"> <xsl:call-template name="entity"> <xsl:with-param name="e" select="."/> </xsl:call-template> </xsl:for-each> </xsl:element> </xsl:when> <xsl:otherwise> <!-- entity--> <xsl:call-template name="entity"> <xsl:with-param name="e" select="."/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:when> <xsl:otherwise> <!-- field--> <xsl:element name="field"> <xsl:attribute name="code"> <xsl:value-of select="name()"/> </xsl:attribute> <xsl:value-of select="normalize-space(.)"/> </xsl:element> </xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:element> </xsl:template> </xsl:stylesheet>
解决方案
问题根源在于<xsl:value-of>会提取文本内容并自动转义,无法保留CDATA结构。要解决这个问题,需要结合cdata-section-elements输出设置和<xsl:copy-of>节点复制功能。
修改后的XSLT代码如下:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> <!-- 指定field元素的内容需用CDATA包裹 --> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" cdata-section-elements="field"/> <xsl:param name="group_fields"/> <xsl:template match="/"> <xsl:element name="btask"> <xsl:for-each select="//btask/*[name()!='section_data' and name()!='section_data_tech' and name()!='instruction']"> <xsl:attribute name="{name()}"> <xsl:value-of select="./text()"/> </xsl:attribute> </xsl:for-each> <xsl:if test="count(//btask/instruction) = 1"> <xsl:element name="instruction"> <xsl:attribute name="code"> <xsl:value-of select="//btask/instruction"/> </xsl:attribute> </xsl:element> </xsl:if> <xsl:for-each select="//btask/*[name()='section_data' or name()='section_data_tech']"> <xsl:call-template name="sectiondata"> <xsl:with-param name="e" select="."/> </xsl:call-template> </xsl:for-each> </xsl:element> </xsl:template> <xsl:template name="sectiondata"> <xsl:param name="e"/> <xsl:copy> <xsl:for-each select="$e/*"> <xsl:call-template name="entity"> <xsl:with-param name="e" select="."/> </xsl:call-template> </xsl:for-each> </xsl:copy> </xsl:template> <xsl:template name="entity"> <xsl:param name="e"/> <xsl:element name="entity_data"> <xsl:attribute name="code"> <xsl:choose> <xsl:when test="string-length($e/@code)"> <xsl:value-of select="$e/@code"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="name()"/> </xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:for-each select="$e/*"> <xsl:choose> <xsl:when test="count(./*)>0 or $group_fields=name()"> <!-- groupdata or entitydata--> <xsl:choose> <xsl:when test="count(./*/*)>0 or $group_fields=name()"> <!-- groupdata--> <xsl:element name="group_data"> <xsl:attribute name="code"> <xsl:value-of select="name()"/> </xsl:attribute> <xsl:for-each select="./*"> <xsl:call-template name="entity"> <xsl:with-param name="e" select="."/> </xsl:call-template> </xsl:for-each> </xsl:element> </xsl:when> <xsl:otherwise> <!-- entity--> <xsl:call-template name="entity"> <xsl:with-param name="e" select="."/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:when> <xsl:otherwise> <!-- field--> <xsl:element name="field"> <xsl:attribute name="code"> <xsl:value-of select="name()"/> </xsl:attribute> <!-- 用copy-of复制原始节点内容,保留CDATA结构 --> <xsl:copy-of select="node()"/> </xsl:element> </xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:element> </xsl:template> </xsl:stylesheet>
修改说明
- 输出配置添加
cdata-section-elements="field":告知XSLT处理器,当field元素的内容包含需要转义的字符时,自动用CDATA标签包裹内容。 - 替换
<xsl:value-of>为<xsl:copy-of>:xsl:value-of仅提取文本并转义,而xsl:copy-of会完整复制原始节点的所有内容(包括CDATA片段),结合cdata-section-elements就能保留原始的CDATA标签格式。
内容的提问来源于stack exchange,提问作者Daniil Baev
相关产品推荐
相关产品推荐

