Menu
CoddyTech

使用 encoding/json 处理结构体

本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 76 课,共 107 课。

encoding/json 包可以让你将 Go 结构体转换为 JSON,也可以将 JSON 转回 Go 结构体。这对于 API、配置文件和数据存储至关重要。结构体标签控制字段与 JSON 键的映射方式。

要将结构体转换为 JSON,请使用 json.Marshal:

type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
    Age   int    `json:"age"`
}

func main() {
    user := User{Name: "Alice", Email: "alice@example.com", Age: 30}
    data, _ := json.Marshal(user)
    fmt.Println(string(data))
    // {"name":"Alice","email":"alice@example.com","age":30}
}

像 `json:"name"` 这样的结构体标签用于指定 JSON 键名。没有标签时,Go 会直接使用字段名。只有导出的字段(首字母大写)才会包含在 JSON 输出中。

要将 JSON 转换回结构体,请使用 json.Unmarshal:

jsonStr := `{"name":"Bob","email":"bob@example.com","age":25}`
var user User
json.Unmarshal([]byte(jsonStr), &user)
fmt.Println(user.Name)  // Bob

常见的标签选项包括 omitempty,用于跳过零值,以及 -,用于完全忽略字段:

type Product struct {
    ID       int    `json:"id"`
    Name     string `json:"name"`
    Price    float64 `json:"price,omitempty"`
    Internal string `json:"-"`
}

使用 omitempty 时,如果 Price 为零,它就不会出现在 JSON 中。无论 Internal 字段的值是什么,它都不会被包含在内。

challenge icon

挑战

简单

让我们构建一个电影目录系统,在 Go 结构体和 JSON 之间进行转换!你将练习把结构体编码为 JSON,以及把 JSON 解码回结构体,并使用结构体标签控制 JSON 字段名称。

你将把代码分成两个文件:

  • movie.go:定义电影数据模型,并添加合适的 JSON 标签。

    创建一个 Movie 结构体,包含以下字段和 JSON 映射:

    • Title(string)映射到 "title"
    • Director(string)映射到 "director"
    • Year(int)映射到 "year"
    • Rating(float64)映射到 "rating",并使用 omitempty,这样未评分的电影就不会显示评分字段
    • InternalID(string)应使用 "-" 完全排除在 JSON 之外

    创建函数 ToJSON(m Movie) string,将 Movie 转换为 JSON 字符串。再创建 FromJSON(jsonStr string) Movie,将 JSON 字符串解析回 Movie 结构体。

  • main.go:演示双向 JSON 转换。

    读取操作类型(encode 或 decode)。

    对于 encode:逐行读取电影详情(title、director、year、rating)。创建一个 InternalID 为 "internal-123" 的 Movie,将其转换为 JSON 并打印结果。

    对于 decode:读取一个 JSON 字符串,将其转换为 Movie 结构体,并按以下格式打印电影详情:

    Title: [title]
    Director: [director]
    Year: [year]
    Rating: [rating]

    评分显示一位小数。如果评分为 0(JSON 中未提供),则打印 Rating: N/A。

输入内容如下:

  • 第 1 行:操作类型(encode 或 decode)
  • 对于 encode:title、director、year、rating(四行)
  • 对于 decode:一个 JSON 字符串(一行)

例如,给定以下输入:

encode
Inception
Christopher Nolan
2010
8.8

输出应为:

{"title":"Inception","director":"Christopher Nolan","year":2010,"rating":8.8}

注意,由于使用了 "-" 标签,输出中不会出现 InternalID。

再比如,给定以下输入:

encode
The Matrix
Wachowskis
1999
0

输出应为:

{"title":"The Matrix","director":"Wachowskis","year":1999}

注意,由于评分为零且使用了 omitempty,rating 会被省略。

再比如,给定以下输入:

decode
{"title":"Interstellar","director":"Christopher Nolan","year":2014,"rating":8.6}

输出应为:

Title: Interstellar
Director: Christopher Nolan
Year: 2014
Rating: 8.6

再比如,给定以下输入:

decode
{"title":"Tenet","director":"Christopher Nolan","year":2020}

输出应为:

Title: Tenet
Director: Christopher Nolan
Year: 2020
Rating: N/A

亲手试试

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// 读取操作类型
	operation, _ := reader.ReadString('\n')
	operation = strings.TrimSpace(operation)

	if operation == "encode" {
		// 读取电影详情
		title, _ := reader.ReadString('\n')
		title = strings.TrimSpace(title)

		director, _ := reader.ReadString('\n')
		director = strings.TrimSpace(director)

		yearStr, _ := reader.ReadString('\n')
		yearStr = strings.TrimSpace(yearStr)
		year, _ := strconv.Atoi(yearStr)

		ratingStr, _ := reader.ReadString('\n')
		ratingStr = strings.TrimSpace(ratingStr)
		rating, _ := strconv.ParseFloat(ratingStr, 64)

		// TODO:用读取的值创建 Movie 结构体
		// 将 InternalID 设置为 "internal-123"
		// 使用 ToJSON 转换为 JSON 并打印结果

	} else if operation == "decode" {
		// 读取 JSON 字符串
		jsonStr, _ := reader.ReadString('\n')
		jsonStr = strings.TrimSpace(jsonStr)

		// TODO:使用 FromJSON 将 JSON 转换为 Movie
		// 按要求的格式打印电影详情
		// 使用 fmt.Sprintf("%.1f", rating) 格式化评分
		// 如果 rating 为 0,则改为打印 "Rating: N/A"

	}
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Go 在线编译器