Go自定义MarshalJSON替换JSON序列化特殊字符失效问题
问题根因
你踩中了Go标准库encoding/json自定义序列化的两个典型坑,结合你描述的“方法内部打印替换成功、最终结果未生效”的现象,根因是以下第二种:
- 若你给类型实现
MarshalJSON时用的是指针接收者,调用json.Marshal时传的是值而非指针,自定义方法根本不会触发。你已经在方法内部加了打印,这个原因可以直接排除。 - 你要么是在完成字符替换后对结果做了二次
json.Marshal调用,要么是构造辅助结构体时没有切断原类型的方法绑定,导致序列化逻辑递归触发;二次序列化时json.Marshal默认开启的HTML转义规则会再次生效,把你替换好的<、>重新转成\u003c、\u003e,等于白做替换。
补充说明:json.Marshal默认会强制转义<、>、&三个HTML特殊字符,这个转义发生在每个字符串值的序列化阶段,手动做字符串替换本质是在转义完成后改结果,只要后续逻辑再触发一次序列化,就会被转义回去,非常不稳定。
正确实现方案
不要手动做字符串替换,手动替换不仅容易遇到二次转义问题,还可能误改JSON结构中非内容部分的字符(比如字段名、其他转义序列里碰巧匹配的片段)。正确做法是从序列化阶段关闭HTML转义,代码如下:
func (t YourStruct) MarshalJSON() ([]byte, error) { // 定义和原结构体同字段的类型别名,切断原类型绑定的MarshalJSON方法,避免无限递归 type Alias YourStruct a := Alias(t) var buf bytes.Buffer enc := json.NewEncoder(&buf) // 核心配置:关闭HTML特殊字符转义 enc.SetEscapeHTML(false) if err := enc.Encode(&a); err != nil { return nil, err } // 去掉Encoder默认追加的末尾换行,保证返回合法JSON res := bytes.TrimRight(buf.Bytes(), "\n") return res, nil }
这个方案的优势:
- 不需要做任何事后字符串替换,序列化阶段就不会主动转义
<、>,从根源避免替换后被还原的问题 - 类型别名的写法完全保留原结构体的字段、tag配置,不需要重复维护字段列表
- 不会破坏原有JSON结构,和默认序列化逻辑除了不转义HTML字符外完全一致
验证
实现上述方法后,直接调用json.Marshal序列化你的结构体,返回结果里的<、>会原样保留,不会再被转义为unicode编码形式。
内容的提问来源于stack exchange,提问作者SpecialUserThisTime
相关产品推荐
相关产品推荐

