You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中使用lxml通过XSLT标识转换时如何保留CDATA?

刚好我之前在处理大体积单行XML格式化时也踩过CDATA丢失的坑,结合你用Python 2.7+XSLT的场景,给你一套可行的解决方案,既能格式化XML,又能完整保留CDATA:

解决方案:XSLT显式保留CDATA + Python流式处理

1. 调整XSLT模板,确保CDATA不被转义

默认的XSLT转换会把CDATA节点转成普通文本,我们需要在模板里做特殊处理,分两种场景:

场景A:知道CDATA所在的元素名

如果你的XML里CDATA固定出现在某些元素(比如<description>、<content>)中,直接在XSLT的输出配置里指定这些元素即可,这是最可靠的方式:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!-- 开启格式化输出,同时指定需要保留CDATA的元素 -->
    <xsl:output method="xml" indent="yes" cdata-section-elements="description content"/>
    
    <!-- 通用复制模板,保留所有节点和属性 -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

场景B:CDATA可能出现在任意元素中

如果CDATA的位置不固定,需要匹配文本中的CDATA标记,手动保留:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" encoding="UTF-8"/>
    
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <!-- 匹配包含CDATA的文本节点,手动输出CDATA标记 -->
    <xsl:template match="text()">
        <xsl:choose>
            <!-- 检查是否是合法的CDATA内容 -->
            <xsl:when test="starts-with(., '&lt;![CDATA[') and ends-with(., ']]&gt;')">
                <xsl:text disable-output-escaping="yes"><xsl:value-of select="."/></xsl:text>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="."/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

2. Python 2.7流式处理大XML文件

因为是GB级的单行XML,绝对不能一次性加载到内存,要用流式解析配合lxml库(Python2.7下推荐用lxml 4.6.5版本,新版本不再支持Python2):

from lxml import etree

def format_large_xml(input_xml_path, output_xml_path, xslt_path):
    # 加载XSLT转换模板
    xslt_tree = etree.parse(xslt_path)
    xslt_transformer = etree.XSLT(xslt_tree)
    
    # 配置XML解析器:保留CDATA,去掉空白文本
    parser = etree.XMLParser(strip_cdata=False, remove_blank_text=True)
    
    with open(output_xml_path, 'wb') as output_file:
        # 流式解析XML,只处理元素结束事件
        for event, elem in etree.iterparse(input_xml_path, events=('end',), parser=parser):
            # 跳过注释和处理指令
            if elem.tag not in (etree.Comment, etree.ProcessingInstruction):
                # 应用XSLT格式化
                formatted_elem = xslt_transformer(elem)
                # 写入格式化后的内容
                output_file.write(etree.tostring(formatted_elem, pretty_print=True, encoding='utf-8'))
            
            # 清理当前节点,释放内存(关键!处理大文件必须做)
            elem.clear()
            # 删除父节点中已处理的兄弟节点
            while elem.getprevious() is not None:
                del elem.getparent()[0]

# 调用示例
if __name__ == '__main__':
    format_large_xml(
        input_xml_path='large_single_line.xml',
        output_xml_path='formatted_with_cdata.xml',
        xslt_path='format_with_cdata.xsl'
    )

注意事项

  • 先安装适配Python2.7的lxml:pip install lxml==4.6.5
  • 优先使用场景A的XSLT配置,场景B的文本匹配可能会误判包含CDATA格式的普通文本
  • 流式处理时的节点清理步骤不能省略,否则内存会快速耗尽

内容的提问来源于stack exchange,提问作者George

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:18:38