xml.escapetext不能直接用于属性值,因为它只转义文本内容中的、&、"、',不处理属性引号边界问题;属性值需用xml.escapestring或交由xml.marshal自动处理。

xml.EscapeText 为什么不能直接用在属性值上
因为 xml.EscapeText 只处理元素内容(即 <tag>content</tag> 中的 content),它会把 、<code>>、&、"、' 转成对应实体,但**不处理属性值里的引号边界问题**。比如你手动拼 <item name="a<b"></item>,即使 a<b> 是 <code>xml.EscapeText 处理过的,双引号仍会提前闭合,导致 XML 语法错误。
正确做法是:属性值必须用 xml.EscapeString(它额外处理引号)或直接交给 xml.Marshal —— 它内部对属性值自动调用 xml.EscapeString,对文本内容调用 xml.EscapeText。
- 别自己拼属性字符串,尤其别混用
xml.EscapeText和双引号包裹 - 如果必须手拼,属性值统一用单引号包裹 +
xml.EscapeString,例如name='<script>'</script> -
xml.EscapeString返回的是已转义字符串,可直接插入属性值位置
encoding/xml.Unmarshal 自动反转义吗
是的,但仅限标准 XML 字符实体(&、、<code>>、"、'),且只在 xml:",chardata" 或普通字段中生效。它**不处理 HTML 实体(如 、©)或自定义 DTD 声明的实体**。
常见错误现象:©Go 解析后仍是 ©Go,不是 ©Go;<div> 能正确变成 <code><div>,但 <code>©(十进制 Unicode 引用)也能识别。
- 源 XML 含非标准实体时,
xml.Unmarshal静默跳过,字段保持原样 - 若需支持 HTML 实体,先用
html.UnescapeString预处理整个 XML 字节流,再传给xml.Unmarshal - CDATA 块内的内容完全不反转义 ——
]]>会被当作文本字符串原样存入xml:",chardata"字段
生成 XML 时中文乱码或缺少声明头
xml.Marshal 输出纯 UTF-8 字节流,不带 XML 声明,也不做任何编码包装。接收方若校验严格(如某些 Java 解析器)或运行在旧 Windows 环境下,可能报“invalid encoding”或显示乱码。
必须手动补全声明头,并确保输出流是 UTF-8 编码(Go 默认就是,但别用其他编码写入文件)。
- 加声明头:拼接
xml.Header + string(data),其中xml.Header是常量"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" - 缩进用
xml.MarshalIndent(v, "", " "),第二个参数留空字符串,否则父级缩进错位 - 别用
html/template生成 XML —— 它会把转成 <code>,破坏结构;换用 <code>text/template - 零值字段(空字符串、0)默认仍输出,加
xml:",omitempty"才跳过
动态属性无法用 encoding/xml 直接生成
encoding/xml 的 struct tag 是静态的,不支持运行时决定属性名和数量。比如你想生成 <user id="1" role="admin" lang="zh-CN"></user>,但属性列表来自 map[string]string,这时 xml:",attr" 无解 —— 它只接受固定字段名绑定。
常见错误尝试:Attrs []xml.Attr 字段加 xml:",attr",结果输出的是 <user><attrs><name>id</name><value>1</value></attrs></user>,而非预期属性。
- 正确方案:改用
text/template,定义模板 - 配套写个自定义函数
xmlEscape,内部调用xml.EscapeString - 不要试图在结构体里塞
[]byte或string拼属性 —— 不安全且难维护
实际项目里最容易被忽略的,是 XML 声明头缺失 + 属性值未用 xml.EscapeString + 动态属性硬塞 struct tag 这三者叠加。单独出问题还能查,一起出现时往往表现为“解析成功但前端渲染异常”或“下游系统拒收”,debug 成本远高于一开始选对方式。











