Beego ORM 查询结果不能直接 json.Marshal:因 Values/Maps 等返回 map[string]interface{} 或 []interface{},含 time.Time 等不支持类型,且字段无 json tag 会导致忽略;应定义专用响应结构体并显式转换,或用 ServeJSON() 配合正确 tag。

Beego ORM 查询结果不能直接 JSON.Marshal?
是的,orm.QuerySeter 返回的 []interface{} 或结构体切片本身不能直接被 json.Marshal 正确序列化——尤其是字段名大小写、空值处理、时间格式、嵌套关系缺失时,JSON 输出常为空对象或报错 json: unsupported type: orm.ParamsList。
根本原因:Beego ORM 的 Values/ValuesList/Maps 等方法返回的是 map[string]interface{} 或 []interface{},其中时间字段是 time.Time,而标准 json 包默认不支持;更常见的是你用了 o.Read(&u) 或 o.LoadRelated 后直接传结构体,但结构体字段没加 json tag,导致全小写字段被忽略。
- 确保模型结构体字段全部以大写字母开头,并显式声明
jsontag(哪怕只是json:"id") - 避免用
ValuesList得到[][]interface{}后硬塞给json.Marshal—— 它连 key 都没有,纯值列表 - 时间字段必须处理:
time.Time默认序列化为 Go 内部格式字符串(含时区信息),前端通常要"2006-01-02T15:04:05Z"或秒级时间戳
struct 转 JSON 最稳妥的方式:定义明确的输出结构体
别复用 ORM 模型结构体直接输出。新建一个只含必要字段、带完整 json tag 和类型控制的结构体,再用 mapstructure 或手动赋值填充。
例如你查用户:
type User struct {
Id int `orm:"auto" json:"id"`
Name string `orm:"size(100)" json:"name"`
Created time.Time `orm:"auto_now_add;type(datetime)" json:"created"`
}
type UserResp struct {
ID int `json:"id"`
Name string `json:"name"`
Created int64 `json:"created"` // 转为时间戳
}
查询后转换:
var users []User
o.QueryTable(&User{}).All(&users)
var res []UserResp
for _, u := range users {
res = append(res, UserResp{
ID: u.Id,
Name: u.Name,
Created: u.Created.Unix(), // 或 .UnixMilli() 看需求
})
}
data, _ := json.Marshal(res)
- 字段名大小写必须匹配
jsontag,而不是结构体字段名(ID→"id") - 不要依赖
json:",string"处理数字,它只对 JSON 解析有效,对Marshal无影响 - 如果字段可能为空(如
*string),需在赋值前判空,否则会 panic
ValuesMaps / Maps 结果怎么安全转 JSON?
Maps() 返回 []orm.Params(即 []map[string]interface{}),看起来像 JSON-ready,但里面的时间仍是 time.Time,且数值可能是 int64、float64,前端 JS 可能出问题(比如 int64 超过 2^53 会丢失精度)。
正确做法:遍历每一项,对 time.Time 和大整数做标准化转换:
var maps []orm.Params
o.QueryTable(&User{}).Maps(&maps)
for i := range maps {
if t, ok := maps[i]["created"].(time.Time); ok {
maps[i]["created"] = t.Unix()
}
if id, ok := maps[i]["id"].(int64); ok {
maps[i]["id"] = int(id) // 转为 int 避免 JS 精度问题(前提是确定不会超 int)
}
}
data, _ := json.Marshal(maps)
-
Maps()不支持LoadRelated,关联数据得自己 join 查询或额外查 -
ValuesList()返回[][]interface{},完全无 key,除非你硬编码字段顺序,否则不建议用于 API 输出 - 所有
interface{}值都应做类型断言,否则json.Marshal可能静默失败或输出 null
Beego 自带的 JSON 输出封装(Controller 层)
在 Controller 中别手写 json.Marshal + Ctx.Output.Body,用 Ctrl.Data["json"] = xxx + Ctrl.ServeJSON(),它内部会调用 json.Marshal 并设好 Content-Type: application/json,还支持 Ctrl.EnableJson 全局开关。
但注意:ServeJSON() 仍受结构体 tag 控制,且默认开启 indent(开发用),线上建议关掉:
func (c *MainController) GetUsers() {
var users []User
o.QueryTable(&User{}).All(&users)
// 转为安全输出结构
c.Data["json"] = wrapUsers(users)
c.ServeJSON(false) // false = no indent
}
-
ServeJSON(true)会格式化输出,体积大、慢,仅调试用 - 若返回 error,别直接
c.Data["json"] = err,应统一包装成map[string]interface{}{"code": 500, "msg": err.Error()} - Beego 2.x 中
orm.Params类型已改名为orm.ParamsMap,升级时注意 import 和类型名
最麻烦的其实是时间字段和关联数据——ORM 查出来的是原始值,JSON 是最终交付格式,中间这层映射必须显式控制,靠自动反射只会埋坑。


















