JSON序列化失败:Go结构体标签语法错误导致无效输出

本文详解go中使用`json.marshal`时因结构体字段标签(struct tag)书写错误(如误用`json:":"text"`)导致json生成异常的问题,并提供正确写法、完整修复示例及关键注意事项。

在使用 Martini(或任何 Go Web 框架)返回 JSON 数据时,若结构体字段的 JSON 标签(json tag)语法不合法,json.Marshal 将无法正确映射字段名,轻则字段丢失、键名异常(如出现 ":" 这类非法键),重则整个字段被忽略,最终输出空对象 {} 或格式错乱的 JSON 字符串——这正是你遇到 "{}{}{}..." 和 {"time":"...","": "Привет"} 等问题的根本原因。

? 问题根源:JSON Tag 语法错误

Go 中结构体字段的 JSON 标签必须严格遵循以下格式:

FieldName Type `json:"key_name[,option]"`

其中:

  • key_name 是生成 JSON 时使用的字段名(字符串字面量);
  • 不能包含未转义的冒号 : —— 你代码中写的 `json:":"text"` 实际被解析为:
    • 字段名部分为空(""),
    • 后续 ":"text" 被视为非法语法,导致标签失效;
  • 正确写法应为:`json:"text"`(双引号包裹键名,无额外冒号)。

❌ 错误示例(全部失效):

Text string `json:":"text"`   // ❌ 冒号位置错误,标签解析失败
User1 string `json:":"user1"` // ❌ 同上

✅ 正确写法:

Text  string `json:"text"`
User1 string `json:"user1"`
Time  string `json:"time"`

✅ 完整修复示例(含最佳实践)

以下是修正后的可运行代码片段,已整合数据库查询、结构体定义与 JSON 序列化逻辑:

type ChatMessage struct {
    Time  string `json:"time"`
    Text  string `json:"text"`
    User1 string `json:"user1"`
}

func getChatData() string {
    db, err := sql.Open("sqlite3", "./database.db")
    if err != nil {
        log.Fatal("DB open failed:", err)
    }
    defer db.Close()

    rows, err := db.Query("SELECT time, text, user1 FROM messages")
    if err != nil {
        log.Fatal("Query failed:", err)
    }
    defer rows.Close()

    var buffer bytes.Buffer
    buffer.WriteByte('[') // 开始 JSON 数组

    first := true
    for rows.Next() {
        var time, text, user1 string
        if err := rows.Scan(&time, &text, &user1); err != nil {
            log.Printf("Scan error: %v", err)
            continue // 跳过单条错误,避免中断整个响应
        }

        msg := ChatMessage{Time: time, Text: text, User1: user1}
        data, err := json.Marshal(msg)
        if err != nil {
            log.Printf("JSON marshal error: %v", err)
            continue
        }

        if !first {
            buffer.WriteByte(',')
        }
        buffer.Write(data)
        first = false
    }
    buffer.WriteByte(']') // 结束 JSON 数组

    return buffer.String()
}
? 关键改进说明:使用 []byte 拼接更高效(bytes.Buffer 已优化);手动构造 JSON 数组 [...],确保前端接收的是合法、可解析的 JSON 数组(而非多个独立 JSON 对象拼接,后者不是标准 JSON);添加错误处理与日志提示,避免 log.Fatal 导致服务崩溃;字段名显式赋值(Time: time),提升可读性与可维护性。

⚠️ 注意事项与建议

  • 永远验证 JSON 输出:在返回前可用 json.Valid(buffer.Bytes()) 检查是否为合法 JSON;
  • 避免手动拼接 JSON 字符串:优先使用 json.Marshal + 切片/数组结构,而非 buffer.WriteString(string(b)),防止注入或编码错误;
  • 字段首字母必须大写:Go 的 json 包仅导出(大写开头)字段参与序列化;
  • Martini 响应建议:直接返回 martini.JSON(200, messages)(若使用 martini-contrib/render),由框架自动处理 Content-Type 与序列化;
  • 时间格式建议:"13:42:21 11.12.14" 非标准 ISO 格式,推荐存储为 time.Time 并用 json:"time,string" 标签实现 RFC3339 自动格式化。

通过修正结构体标签语法并采用规范的 JSON 构建方式,你的 API 将稳定输出符合标准的 JSON 数据,彻底解决 {"": "..."} 或 {} 等异常现象。