beego的c.servejson()默认过滤零值字段(0、""、false、nil),需传false禁用;小写字段名因未导出被json包忽略,与零值过滤无关;嵌套类型空值判断有特定规则;输入为空多因解析失败而非过滤。

Beego的c.ServeJSON()默认会删掉零值字段
Beego 的 c.ServeJSON() 不是简单调用 json.Marshal(),而是在序列化前主动过滤掉值为 0、""、false、nil 的字段。这不是 bug,是框架设计行为。现象就是:结构体里明明写了 Age: 0 或 Status: "",返回 JSON 里直接没这个 key。
关键点在于:这个过滤无法通过结构体 tag(比如 omitempty)关闭,也不是因为字段没导出——它发生在 json.Marshal() 之前,是 Beego 自己加的一层逻辑。
- 必须显式传参禁用:
c.ServeJSON(false),第二个参数false表示跳过零值过滤 -
c.ServeJSON(true)和无参调用等价,都是开启过滤 - 该开关只对本次调用生效,不能全局配置
结构体字段小写会导致整个字段消失,和零值过滤无关
如果字段名首字母小写(如 name string),Go 的 json 包根本无法反射访问,json.Marshal() 输出里连 key 都不会出现。这和 Beego 的零值过滤是两回事,但现象相似,容易混淆。
正确做法只有两个条件同时满足:
- 字段名首字母大写(
Name string) - 若需小写 JSON key,用
jsontag 显式声明:Name string `json:"name"`
别指望靠 form 或 orm tag 影响 JSON 输出,它们互不干扰。
嵌套结构体、map、切片的“空”判断更宽松
Beego 对复合类型的零值判断不是简单判 nil,而是有具体规则:
-
map[string]interface{}值为nil→ 被删;空 map(map[string]interface{}{})→ 保留为{} - 嵌套结构体指针为
nil→ 整个字段被删;若为非-nil 空结构体(如&User{})→ 按其内部字段是否零值再决定 - 切片为
nil→ 被删;空切片([]string{})→ 保留为[]
这些行为在 c.ServeJSON(false) 下依然保持原生 json 包语义,Beego 不会额外干预。
接收 JSON 时字段为空,大概率是解析失败而非零值问题
POST 接收 JSON 数据为空,常见原因不是零值被过滤,而是解析环节就断了:
- 没在
conf/app.conf里设copyrequestbody = true→this.Ctx.Input.RequestBody永远为空 - 结构体字段没导出(小写开头)或漏写
jsontag →json.Unmarshal()无法赋值,字段保持零值 - 误用
ParseForm()解析 JSON → 它只处理application/x-www-form-urlencoded,对application/json完全无效
真正容易被忽略的是:零值过滤只影响输出,不影响输入;而输入失败时,你看到的“空”其实是未初始化的零值,不是被删掉的字段。











