Go自定义JSON序列化:自定义Marshal中用encoder.Encode会栈溢出吗?
问题解答
1. 自定义Marshal中使用encoder.Encode会导致栈溢出吗?
会。因为json.Encoder.Encode内部会调用目标类型的MarshalJSON方法(也就是你自定义的MyForm.MarshalJSON),这会形成无限递归调用:自定义Marshal函数调用Encode,Encode又回头触发你的Marshal逻辑,循环往复直到goroutine栈空间耗尽,最终触发栈溢出错误。
2. 外部与内部调用结果不同的原因
外部调用(第135行)用的是json.MarshalIndent,会正常执行你的自定义Marshal逻辑(无递归时);而内部调用(第173行)触发了无限递归,要么直接栈崩溃,要么在os.Exit(0)强制退出前只输出了部分内容,自然和外部正常执行的结果完全不同。
3. 实现需求的正确方式
要实现MyForm序列化时fields数组每个条目单独一行、整体保持美化格式的效果,不能用递归调用Encoder的方式,正确做法是手动构建JSON结构,示例代码如下:
func (f MyForm) MarshalJSON() ([]byte, error) { // 定义辅助结构体,序列化除fields外的基础字段 type FormAux struct { Name string `json:"name"` Method string `json:"method"` } aux := FormAux{ Name: f.Name, Method: f.Method, } // 序列化基础部分,获取带缩进的JSON字节 auxBytes, err := json.MarshalIndent(aux, "", " ") if err != nil { return nil, err } // 单独处理fields数组:每个Field序列化后单独换行并对齐缩进 var fieldsBytes []byte fieldsBytes = append(fieldsBytes, []byte(`,"fields": [`)...) for i, field := range f.Fields { fieldBytes, err := json.MarshalIndent(field, "", " ") if err != nil { return nil, err } // 每个字段前添加换行和缩进 fieldsBytes = append(fieldsBytes, []byte("\n ")...) fieldsBytes = append(fieldsBytes, fieldBytes...) // 非最后一个字段添加逗号分隔 if i != len(f.Fields)-1 { fieldsBytes = append(fieldsBytes, ',') } } // 闭合数组和结构体 fieldsBytes = append(fieldsBytes, []byte("\n]")...) // 拼接基础字段和fields部分的JSON auxBytes = auxBytes[:len(auxBytes)-1] result := append(auxBytes, fieldsBytes...) result = append(result, '}') return result, nil }
效果说明
- 基础字段(name、method)保持正常的美化缩进层级
fields数组的每个元素单独占一行,与整体格式对齐- 完全避免递归调用,不会出现栈溢出问题
内容的提问来源于stack exchange,提问作者xpt
相关产品推荐
相关产品推荐

