Menu
CoddyTech

JSON 持久化层

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

challenge icon

挑战

简单

让我们的图书馆管理系统支持数据持久化吧!你将实现一个 JSON 持久化层,把图书馆的数据转换为 JSON 格式以便保存,并能从 JSON 字符串中加载数据。这对于任何需要在不同会话之间保留数据的实际应用都至关重要。

你将把代码分到 5 个文件中:

  • book.go:定义带有 JSON 结构体标签的 Book 结构体,以控制序列化。每个字段都应有对应的 JSON 标签:id、title、author、isbn 和 available。还要包含 NewBook 构造函数。
  • user.go:定义带有 JSON 结构体标签的 User 结构体,标签分别为 id、name、email 和 borrowed_books。还要包含 NewUser 构造函数。
  • data.go:创建一个 LibraryData 结构体,作为 JSON 序列化的数据传输对象。它应有两个字段:Books(Book 指针切片)和 Users(User 指针切片),每个字段都要有合适的 JSON 标签(books 和 users)。
  • library.go:定义用于存放图书和用户映射的 Library 结构体。新增两个方法:
    • ToJSON():使用 json.MarshalIndent 将图书馆的数据转换为格式化的 JSON 字符串,并使用两个空格缩进。该方法应从映射中收集所有图书和用户,放入 LibraryData 结构体,然后进行序列化。
    • FromJSON(jsonStr string):接收一个 JSON 字符串,将其反序列化到 LibraryData 结构体中,并用加载的图书和用户填充图书馆的映射。
  • main.go:通过创建图书馆并将其转换为 JSON,演示持久化层的用法。

    读取图书数量,然后依次读取每本图书的 ID、标题、作者和 ISBN。接着读取用户数量,然后依次读取每位用户的 ID、姓名和电子邮箱。填充图书馆后,调用 ToJSON() 并打印生成的 JSON 字符串。

输入将按以下格式提供:

  • 图书数量,然后是每本图书的 ID、标题、作者和 ISBN(每项各占一行)
  • 用户数量,然后是每位用户的 ID、姓名和电子邮箱(每项各占一行)

例如,输入:

2
B001
The Go Programming Language
Alan Donovan
978-0134190440
B002
Clean Code
Robert Martin
978-0132350884
1
U001
Alice Smith
alice@library.com

输出应为:

{
  "books": [
    {
      "id": "B001",
      "title": "The Go Programming Language",
      "author": "Alan Donovan",
      "isbn": "978-0134190440",
      "available": true
    },
    {
      "id": "B002",
      "title": "Clean Code",
      "author": "Robert Martin",
      "isbn": "978-0132350884",
      "available": true
    }
  ],
  "users": [
    {
      "id": "U001",
      "name": "Alice Smith",
      "email": "alice@library.com",
      "borrowed_books": []
    }
  ]
}

再例如,输入:

1
B100
Design Patterns
Gang of Four
978-0201633610
0

输出应为:

{
  "books": [
    {
      "id": "B100",
      "title": "Design Patterns",
      "author": "Gang of Four",
      "isbn": "978-0201633610",
      "available": true
    }
  ],
  "users": []
}

重要:遍历映射以构建切片时,应按以下顺序添加图书:B001、B002 等(按 ID 的字母顺序排序),用户也要按相同方式排序。这样可以确保 JSON 输出一致。你可以先对映射的键使用 sort.Strings 排序,再进行遍历。

LibraryData 结构体连接了基于映射的存储方式与 JSON 基于数组的格式。这种传输对象模式常用于序列化复杂的数据结构!

亲手试试

package main

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

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

	// 创建一个新库
	library := NewLibrary()

	// 读取图书数量
	numBooksStr, _ := reader.ReadString('\n')
	numBooks, _ := strconv.Atoi(strings.TrimSpace(numBooksStr))

	// TODO: 读取每本书的详细信息(ID、书名、作者、ISBN)
	// 并将它们添加到库中
	for i := 0; i < numBooks; i++ {
		// TODO: 读取图书 ID
		// TODO: 读取书名
		// TODO: 读取图书作者
		// TODO: 读取图书 ISBN
		// TODO: 创建图书并添加到库中
	}

	// 读取用户数量
	numUsersStr, _ := reader.ReadString('\n')
	numUsers, _ := strconv.Atoi(strings.TrimSpace(numUsersStr))

	// TODO: 读取每位用户的详细信息(ID、姓名、邮箱)
	// 并将它们添加到库中
	for i := 0; i < numUsers; i++ {
		// TODO: 读取用户 ID
		// TODO:读取用户姓名
		// TODO:读取用户邮箱
		// TODO:创建用户并添加到库中
	}

	// TODO:将库转换为 JSON 并输出
	fmt.Println(library.ToJSON())
}

面向对象编程中的所有课

自己练习: Go 在线编译器