go的encoding/xml包默认丢弃cdata和注释:cdata被合并为chardata,注释则完全忽略;手动解析需字符串匹配提取cdata,注释须用第三方库或预处理;序列化时cdata只能手动拼接。

Go 的 encoding/xml 默认会丢弃 CDATA 和注释
Go 标准库的 encoding/xml 包在解析 XML 时,不会保留 CDATA 节点或 XML 注释。它把 当作普通字符数据(CharData)合并进相邻的文本中,而注释(<!-- ... -->)则被完全跳过——既不触发回调,也不出现在 xml.Token 流里。
用 xml.Decoder.Token() 手动捕获 CDATA 内容
虽然 xml.Unmarshal 不支持 CDATA,但底层的 xml.Decoder 在遇到 xml.CharData 时,会原样返回其原始字节。如果 XML 中 CDATA 前后没有换行/空格干扰,且你控制输入格式,可以靠识别起始/结束标记粗略提取:
decoder := xml.NewDecoder(reader)
for {
token, err := decoder.Token()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
switch t := token.(type) {
case xml.CharData:
data := string(t)
if strings.HasPrefix(data, "<![CDATA[") && strings.HasSuffix(data, "]]>") {
cdataContent := data[9 : len(data)-3] // 去掉 <![CDATA[ 和 ]]>
fmt.Println("Found CDATA:", cdataContent)
}
}
}- 这仅适用于 CDATA 独占一个
xml.CharData令牌的情况;若被空格、换行或相邻文本拆分,会失效 -
xml.CharData也可能包含普通文本,必须靠字符串匹配判断,不可依赖类型 - 标准库不提供
xml.CDATA令牌类型,所以没有更“干净”的方式
注释完全不可见,需改用第三方解析器或预处理
encoding/xml 的 Token() 方法永远不会返回 xml.Comment 类型,也没有任何配置开关能开启注释支持。如果你必须读取注释:
- 用正则预处理 XML 字符串(例如
regexp.MustCompile(`<!--[\s\S]*?-->`)提前提取),但会破坏结构化解析流程 - 切换到支持注释的库,如
github.com/kylelemons/godebug/pretty不适用;真正可用的是github.com/antchfx/xmlquery(基于net/xml扩展)或纯文本解析器github.com/jbowtie/gokogiri - 最稳妥的做法是:确认业务是否真需要注释内容——多数生产场景中,XML 注释属于元信息,不应参与逻辑处理
写入时 CDATA 只能靠手动拼接字符串
xml.Marshal 和 xml.Encoder.Encode 都不支持生成 CDATA。若必须输出 ,只能绕过标准序列化,用 fmt.Fprintf 或 strings.Builder 拼接:
立即学习“go语言免费学习笔记(深入)”;
var b strings.Builder
b.WriteString("<root>")
b.WriteString("<content><![CDATA[<script>alert(1)</script>]]></content>")
b.WriteString("</root>")
fmt.Println(b.String())- 这样做会失去类型安全和嵌套结构校验,容易拼错标签或转义错误
- 若内容含
]]>,CDATA 将提前终止,必须提前检测并报错或拒绝 - 无法与 struct tag(如
xml:"content")协同工作,意味着你要放弃xml.Marshal整体流程
真正棘手的地方不在语法怎么写,而在于 CDATA 和注释本质上是 XML 的“非信息性”成分——Go 的设计哲学倾向忽略它们,除非你明确选择脱离标准库走自定义解析路线。










