
go 的 encoding/xml 包默认不解析 dtd 中声明的自定义实体(如 &n;),需手动配置 decoder 的 entity 映射表才能正确解码含实体引用的 xml 文档。
在 Go 中解析包含自定义 XML 实体(例如 )的文档时,若直接使用 xml.Unmarshal 或 xml.NewDecoder 默认实例,会触发类似 XML syntax error: invalid character entity &n; 的错误。这是因为 Go 标准库的 encoding/xml 包不自动解析 DTD 声明,也不会将 定义自动注入实体映射表——它仅预置了少量 HTML 公共实体(如 &, zuojiankuohaophpcn),且该内置映射是全局共享、不可安全修改的。
解决方法是:显式创建 xml.Decoder 实例,并为其 Entity 字段赋值一个自定义 map[string]string,将实体名(不含 & 和 ;)映射到其展开后的字符串值。
以下是一个完整示例:
package main
import (
"bytes"
"encoding/json"
"encoding/xml"
"fmt"
)
type JMdict struct {
Entries []Entry `xml:"entry"`
}
type Entry struct {
Pos string `xml:"pos"`
}
func main() {
// 示例 XML 片段(含自定义实体 &n;)
str := `
&n;
`
jmd := JMdict{}
// 创建 Decoder 并注入自定义实体映射
d := xml.NewDecoder(bytes.NewReader([]byte(str)))
d.Entity = map[string]string{
"n": "noun (common) (futsuumeishi)",
}
err := d.Decode(&jmd)
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
// 验证解析结果
jsonBytes, _ := json.MarshalIndent(jmd, "", " ")
fmt.Println(string(jsonBytes))
}✅ 输出结果中 Pos 字段将正确为 "noun (common) (futsuumeishi)",而非原始 &n;。
⚠️ 注意事项:
- Entity 映射键为实体名(如 "n"),不带 & 和 ;;
- 若 XML 中存在多个自定义实体(如 &v;, &adj;),需全部预先注册到 d.Entity 中;
- xml.Unmarshal 无法直接设置 Entity,必须使用 xml.Decoder 手动控制;
- 不建议修改 xml.HTMLAutoClose 或全局 xml.HTMLEntity,因其影响所有后续解析,且非线程安全;
- 若需完整 DTD 支持(如参数实体、外部子集),Go 标准库目前不支持,应考虑预处理 XML(如用 xslt 或正则替换)或切换至支持 DTD 的专用 XML 库。
总结:Go 的 XML 解析器设计偏向轻量与安全,默认放弃 DTD 处理以避免实体爆炸、外部实体注入等风险。对于可控场景下的简单实体替换,通过 Decoder.Entity 显式注入是最简洁、可靠且符合 Go 惯用法的解决方案。









