
本文详解 go 语言中因结构体 json 标签语法错误导致的序列化异常,重点纠正 `json:":"field"` 这类常见误写,确保 `json.marshal` 正确生成标准 json 字段名。
在使用 Martini(或任何基于 Go 的 Web 框架)返回数据库查询结果为 JSON 时,若响应出现字段名异常(如 ":"text")、空对象 {} 或非法 JSON(如多段无分隔的 JSON 拼接),绝大多数情况源于结构体字段的 JSON 标签(struct tag)书写错误。
你提供的两段代码中,关键问题都出在结构体定义:
// ❌ 错误写法:多了一个冒号,语法不合法 Text string `json:":"text"` // 实际解析为键名 `":"text"`,而非 `"text"`
Go 的 struct tag 语法要求:json:"key" 是标准格式;json:":"text" 是无效语法——它会被 encoding/json 包忽略或误解析为字面量 ":"text",导致字段无法正确映射,甚至被跳过(表现为输出空对象 {})。
✅ 正确写法应为:
type ChatBetweenUsers struct {
Time string `json:"time"` // 字段 Time 序列化为 JSON 键 "time"
Text string `json:"text"` // ✅ 不是 `json:":"text"`
User1 string `json:"user1"` // ✅ 不是 `json:":"user1"`
}⚠️ 注意:结构体字段名首字母必须大写(即导出字段),否则 json.Marshal 无法访问该字段,始终输出空值或忽略。
此外,你的当前逻辑存在另一个严重问题:将多个 JSON 对象直接拼接成字符串(如 {"a":1}{"b":2}),这并非合法的 JSON 数组,前端 JSON.parse() 会报错。Martini(及标准 HTTP API)期望返回的是单个、有效的 JSON 值,推荐做法是:
- 将所有记录收集到切片中,再整体序列化为 JSON 数组;
- 避免手动拼接字符串。
修正后的完整示例(兼容 Martini):
type ChatBetweenUsers struct {
Time string `json:"time"`
Text string `json:"text"`
User1 string `json:"user1"`
}
// 在路由处理函数中:
func getMessages() string {
db, err := sql.Open("sqlite3", "./database.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
rows, err := db.Query("SELECT time, text, user1 FROM messages;")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
var messages []ChatBetweenUsers
for rows.Next() {
var time, text, user1 string
if err := rows.Scan(&time, &text, &user1); err != nil {
log.Fatal(err)
}
messages = append(messages, ChatBetweenUsers{
Time: time,
Text: text,
User1: user1,
})
}
// ✅ 一次性序列化整个切片 → 得到合法 JSON 数组:[{"time":"...","text":"..."}, ...]
b, err := json.Marshal(messages)
if err != nil {
log.Fatal(err)
}
return string(b)
}? 总结关键点:
- JSON tag 必须严格遵循 `json:"field_name"` 格式,禁止额外冒号、引号或空格;
- 所有需序列化的字段名首字母必须大写;
- 避免逐个 json.Marshal + 字符串拼接,改用切片收集后统一序列化,保证输出为标准 JSON(数组或对象);
- 开发时可借助 Go Playground 示例 快速验证 struct tag 行为。
遵循以上规范,即可彻底解决 Martini(及任意 Go HTTP 服务)中 JSON 输出混乱、字段丢失或解析失败的问题。










