encoding/json với struct
Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 76 trên 107.
Package encoding/json giúp bạn chuyển đổi struct của Go sang JSON và ngược lại. Đây là điều thiết yếu khi làm việc với API, tệp cấu hình và lưu trữ dữ liệu. Struct tag kiểm soát cách các trường ánh xạ sang khóa JSON.
Để chuyển đổi một struct sang JSON, hãy dùng 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}
}Các thẻ struct như `json:"name"` chỉ định tên khóa JSON. Nếu không có thẻ, Go dùng trực tiếp tên trường. Chỉ các trường được xuất (viết hoa chữ cái đầu) mới có trong đầu ra JSON.
Để chuyển JSON trở lại thành một struct, hãy dùng json.Unmarshal:
jsonStr := `{"name":"Bob","email":"bob@example.com","age":25}`
var user User
json.Unmarshal([]byte(jsonStr), &user)
fmt.Println(user.Name) // BobCác tùy chọn tag thường dùng gồm omitempty để bỏ qua giá trị không và - để hoàn toàn bỏ qua một trường:
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price,omitempty"`
Internal string `json:"-"`
}Với omitempty, nếu Price bằng 0, trường này sẽ không xuất hiện trong JSON. Trường Internal không bao giờ được đưa vào, bất kể giá trị của nó là gì.
Thử thách
DễHãy xây dựng một hệ thống danh mục phim có thể chuyển đổi giữa struct của Go và JSON! Bạn sẽ luyện tập cách marshal struct thành JSON và unmarshal JSON trở lại thành struct, đồng thời dùng thẻ struct để kiểm soát tên trường JSON.
Bạn sẽ sắp xếp code trong hai tệp:
movie.go: Định nghĩa mô hình dữ liệu phim với các thẻ JSON phù hợp.Tạo một struct
Movievới các trường và ánh xạ JSON sau:Title(string) ánh xạ thành"title"Director(string) ánh xạ thành"director"Year(int) ánh xạ thành"year"Rating(float64) ánh xạ thành"rating"vớiomitemptyđể phim chưa được đánh giá không hiển thị trường ratingInternalID(string) phải được loại khỏi JSON hoàn toàn bằng cách dùng"-"
Tạo một hàm (function)
ToJSON(m Movie) stringđể chuyển một Movie thành chuỗi JSON. Đồng thời tạoFromJSON(jsonStr string) Movieđể phân tích chuỗi JSON và chuyển thành struct Movie.main.go: Minh họa cách chuyển đổi JSON theo cả hai chiều.Đọc loại thao tác (
encodehoặcdecode).Với
encode: Đọc thông tin phim (title, director, year, rating), mỗi thông tin trên một dòng riêng. Tạo một Movie có InternalID là"internal-123", chuyển thành JSON rồi in kết quả ra.Với
decode: Đọc một chuỗi JSON, chuyển thành struct Movie rồi in thông tin phim theo định dạng sau:Title: [title] Director: [director] Year: [year] Rating: [rating]Hiển thị rating với một chữ số thập phân. Nếu rating là 0 (không có trong JSON), hãy in
Rating: N/A.
Đầu vào sẽ có định dạng sau:
- Dòng 1: Loại thao tác (
encodehoặcdecode) - Với encode: title, director, year, rating (bốn dòng)
- Với decode: một chuỗi JSON (một dòng)
Ví dụ, với đầu vào:
encode
Inception
Christopher Nolan
2010
8.8Kết quả đầu ra sẽ là:
{"title":"Inception","director":"Christopher Nolan","year":2010,"rating":8.8}Lưu ý rằng InternalID không xuất hiện trong đầu ra vì có thẻ "-".
Với đầu vào:
encode
The Matrix
Wachowskis
1999
0Kết quả đầu ra sẽ là:
{"title":"The Matrix","director":"Wachowskis","year":1999}Lưu ý rằng rating bị lược bỏ vì giá trị bằng 0 và có omitempty.
Với đầu vào:
decode
{"title":"Interstellar","director":"Christopher Nolan","year":2014,"rating":8.6}Kết quả đầu ra sẽ là:
Title: Interstellar
Director: Christopher Nolan
Year: 2014
Rating: 8.6Với đầu vào:
decode
{"title":"Tenet","director":"Christopher Nolan","year":2020}Kết quả đầu ra sẽ là:
Title: Tenet
Director: Christopher Nolan
Year: 2020
Rating: N/ATự làm thử
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Đọc loại thao tác
operation, _ := reader.ReadString('\n')
operation = strings.TrimSpace(operation)
if operation == "encode" {
// Đọc thông tin phim
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: Tạo struct Movie với các giá trị đã đọc
// Đặt InternalID thành "internal-123"
// Dùng ToJSON để chuyển sang JSON và in kết quả
} else if operation == "decode" {
// Đọc chuỗi JSON
jsonStr, _ := reader.ReadString('\n')
jsonStr = strings.TrimSpace(jsonStr)
// TODO: Dùng FromJSON để chuyển JSON thành Movie
// In thông tin phim theo định dạng yêu cầu
// Dùng fmt.Sprintf("%.1f", rating) để định dạng rating
// Nếu rating bằng 0, hãy in "Rating: N/A" thay thế
}
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng OOP trong Go
Tệp bên ngoàiWorkspace và module trong GoPackage và importTên được export và không được exportGiới thiệu về OOP trong GoStruct như lớpĐịnh nghĩa phương thức cho structReceiver kiểu con trỏ và kiểu giá trịKhởi tạo structHàm khởi tạoÔn tập: máy tính đơn giản4Interface
Giới thiệu về interfaceTriển khai ngầm địnhInterface như một hợp đồngInterface rỗng (any)Khẳng định kiểuswitch kiểuKết hợp interfaceInterface Stringer và ErrorÔn tập: Máy tính hình học7Tính đóng gói
Trường được xuất và không được xuấtĐóng gói ở cấp packagePhương thức getter và setterẨn thông tin trong GoÔn tập: hồ sơ học sinh10Generics (Go 1.18 trở lên)
Giới thiệu về GenericsTham số kiểuRàng buộc kiểuStruct tổng quátGiải pháp thay thế cho phương thức tổng quátÔn tập: tập hợp tổng quát13Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếMẫu SingletonMẫu FactoryMẫu Abstract FactoryMẫu ObserverMẫu Strategy2Tìm hiểu chuyên sâu về kiểu dữ liệu và struct
Kiểu cơ bản và kiểu tổng hợpĐịnh nghĩa kiểu dữ liệu tùy chỉnhThẻ structStruct ẩn danhStruct lồng nhauGiá trị zero và giá trị mặc địnhÔn tập: sổ liên lạc5Ưu tiên kết hợp hơn kế thừa
Vì sao Go không có kế thừaNhập môn nhúng structTự động nâng phương thứcNhúng nhiều structNhúng và kết tậpChe khuất phương thức được nhúngÔn tập: hệ phân cấp nhân viên8Xử lý lỗi và lập trình hướng đối tượng
Giao diện errorCác kiểu lỗi tùy chỉnhBọc lỗi với fmt.ErrorfLỗi sentinelerrors.Is() và errors.As()Panic, defer và recoverÔn tập: trình phân tích tệp11Thư viện chuẩn và OOP
io.Reader và io.Writersort.Interfaceinterface fmt.Stringerencoding/json với structinterface http.HandlerÔn tập: mô hình REST API3Con trỏ và bộ nhớ
Cơ bản về con trỏ trong GoCon trỏ đến structTruyền theo giá trị và tham chiếuHàm new()Thu gom rác trong GoÔn tập: xây dựng danh sách liên kết6Đa hình trong Go
Đa hình qua interfaceĐịnh kiểu vịt trong GoQuy tắc thỏa mãn interfaceCác tập hợp đa hìnhTiêm phụ thuộcÔn tập: bộ xử lý thanh toán9Lập trình đồng thời và lập trình hướng đối tượng
Cơ bản về goroutineChannel và giao tiếpChannel có bộ đệm và không có bộ đệmCâu lệnh selectsync.Mutex và sync.RWMutexsync.WaitGroupThiết kế struct an toàn luồngÔn tập: worker poolTự luyện tập: Trình biên dịch Go online