17370845950

Go 中处理自定义 XML 实体的正确方法

go 标准库 `encoding/xml` 默认不解析 dtd 中声明的实体(如 ``)遇到 `&n;` 会报“invalid character entity”错误;需手动初始化 `xml.decoder` 并设置 `entity` 映射表,才能正确解析自定义实体。

在 Go 中解析含自定义 XML 实体(例如 )的文档时,直接使用 xml.Unmarshal 或 xml.NewDecoder(reader).Decode() 会失败,并抛出类似 XML syntax error: invalid character entity &n; 的错误。这是因为 Go 的 encoding/xml 包不自动解析 DTD,也不会从文档中提取并注册 声明——它仅提供一个可由调用方预设的 Entity map[string]string 字段,用于静态映射实体名到其替换文本。

要正确解析,必须显式创建 *xml.Decoder,并在调用 Decode() 前为其 Entity 字段赋值。例如:

func parseWithEntities(xmlData string) error {
    type JMdict struct {
        Pos string `xml:"pos"`
    }
    var jmd JMdict

    decoder := xml.NewDecoder(strings.NewReader(xmlData))
    // 手动注册实体:key 是实体名(不含 & 和 ;),value 是实际替换内容
    decoder.Entity = map[string]string{
        "n": "noun (common) (futsuumeishi)",
        "v": "verb (godan)",
        "adj": "adjective (keiyoushi)",
    }

    if err := decoder.Decode(&jmd); err != nil {
        return fmt.Errorf("XML decode failed: %w", err)
    }

    fmt.Printf("Parsed pos: %q\n", jmd.Pos) // 输出: "noun (common) (futsuumeishi)"
    return nil
}

⚠️ 注意事项:

  • 实体名(如 n)不能包含 & 或 ;,只需写键名本身;
  • decoder.Entity 是浅层映射,不支持嵌套实体或参数化实体(如 &entity1; 引用 &entity2;);
  • HTML 内置实体(如 &, zuojiankuohaophpcn)始终可用,无需重复定义;
  • 若 XML 文档含多个 DTD 实体,需全部手动列出,建议从 DTD 中提取后统一维护;
  • xml.Unmarshal 等便捷函数无法设置 Entity,必须使用 xml.D

    ecoder 流式解析路径。

总结:Go 的 XML 解析器设计强调安全与确定性,主动放弃自动 DTD 处理以避免外部实体攻击和全局状态污染。因此,处理自定义实体是明确的、显式的职责——开发者需承担解析前的实体映射工作,这也使行为更可控、更易测试。