JSON sang Go Struct
Tạo Go struct từ JSON
Đầu vào JSON
Đầu ra Go
Chuyển đổi JSON sang Go là gì?
Chuyển đổi JSON sang Go biến dữ liệu JSON thô thành các định nghĩa kiểu Go hoạt động với package encoding/json trong thư viện chuẩn của Go. Go là ngôn ngữ kiểu tĩnh, vì vậy mỗi trường JSON cần một trường struct tương ứng với kiểu đúng và một struct tag để nói cho bộ tuần tự hóa biết cần ánh xạ khóa JSON nào. Việc viết tay các định nghĩa này cho các phản hồi API lớn hoặc lồng nhau sâu rất chậm và dễ mắc lỗi.
Package encoding/json, được định nghĩa trong đặc tả Go từ Go 1.0, sử dụng reflection để khớp các khóa JSON với các trường struct được xuất. Việc khớp mặc định không phân biệt chữ hoa chữ thường, nhưng việc dùng json struct tag tường minh là thông lệ chuẩn vì nó loại bỏ sự mơ hồ và cho phép bạn dùng quy ước PascalCase của Go trong khi JSON vẫn ở dạng camelCase hoặc snake_case. Một bộ chuyển đổi tự động hóa điều này: nó đọc JSON của bạn, suy luận kiểu Go từ các giá trị và xuất ra định nghĩa struct với các tag đúng.
Hệ thống kiểu của Go ánh xạ gọn gàng sang JSON. Chuỗi trở thành string, boolean trở thành bool, số nguyên trở thành int, và số thực dấu phẩy động trở thành float64. Các đối tượng JSON lồng nhau trở thành các struct có tên riêng biệt, còn mảng trở thành slice. Điểm khác biệt duy nhất là null: Go không có kiểu nullable chung, nên các giá trị null thường trở thành kiểu con trỏ (*string, *int) hoặc interface{}. Một bộ tạo xử lý tất cả điều này trong vài mili giây.
Tại sao dùng bộ chuyển đổi JSON sang Go?
Định nghĩa Go struct từ JSON bằng tay nghĩa là đếm dấu ngoặc nhọn, đoán kiểu và viết lại tag mỗi khi API thay đổi. Một bộ chuyển đổi loại bỏ sự phiền toái đó.
Các trường hợp sử dụng JSON sang Go
Ánh xạ kiểu từ JSON sang Go
Package encoding/json tuân theo các quy tắc cụ thể khi ánh xạ giá trị JSON sang kiểu Go. Bảng dưới đây cho thấy ánh xạ mặc định và các lựa chọn thay thế phổ biến. Cột "Mặc định" là những gì hầu hết các bộ tạo xuất ra. Cột "Thay thế" cho thấy các kiểu bạn có thể chọn dựa trên yêu cầu, chẳng hạn int64 cho các ID lớn hoặc kiểu con trỏ cho các trường nullable.
| Kiểu JSON | Ví dụ | Mặc định | Thay thế |
|---|---|---|---|
| string | "hello" | string | string |
| number (integer) | 42 | int | int64 |
| number (float) | 3.14 | float64 | float64 |
| boolean | true | bool | bool |
| null | null | interface{} | *string / *int |
| object | {"k": "v"} | struct | struct |
| array of strings | ["a", "b"] | []string | []string |
| array of objects | [{"id": 1}] | []Item | []Item |
| mixed array | [1, "a"] | []interface{} | []interface{} |
Tham khảo Go JSON Struct Tag
Struct tag kiểm soát cách encoding/json tuần tự hóa và giải tuần tự hóa các trường. Tag json là tag phổ biến nhất, nhưng bạn có thể kết hợp nó với các tag khác (db, yaml, xml) trên cùng một trường. Cú pháp tag là một chuỗi được giới hạn bằng dấu backtick sau kiểu trường. Dưới đây là các tùy chọn json tag mà encoding/json hỗ trợ.
| Tag | Hành vi | Trường hợp sử dụng |
|---|---|---|
| json:"name" | Maps struct field to JSON key "name" | Always generated |
| json:"name,omitempty" | Omits field from output if zero value | Optional fields |
| json:"-" | Field is never serialized or deserialized | Internal fields |
| json:"name,string" | Encodes int/bool as JSON string | String-encoded numbers |
json.Unmarshal so với json.NewDecoder
Go cung cấp hai cách giải mã JSON: json.Unmarshal cho slice byte và json.NewDecoder cho luồng io.Reader. Cả hai đều dùng cùng quy tắc struct tag, nhưng khác nhau ở chỗ nên dùng khi nào.
Ví dụ mã
Các ví dụ này cho thấy cách sử dụng Go struct được tạo từ JSON trong các chương trình thực tế, cùng cách tạo chúng từ các ngôn ngữ khác. Các ví dụ Go sử dụng encoding/json từ thư viện chuẩn.
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
IsActive bool `json:"is_active"`
Tags []string `json:"tags"`
}
func main() {
data := []byte(`{"id":1,"name":"Alice","email":"alice@example.com","is_active":true,"tags":["admin","editor"]}`)
var user User
if err := json.Unmarshal(data, &user); err != nil {
panic(err)
}
fmt.Println(user.Name) // → Alice
fmt.Println(user.Tags) // → [admin editor]
}// JSON response from API — this is the shape you'd convert to Go:
const res = await fetch("https://api.example.com/users/1");
const user = await res.json();
// user → { "id": 1, "name": "Alice", "email": "alice@example.com" }
// Equivalent Go struct:
// type User struct {
// ID int `json:"id"`
// Name string `json:"name"`
// Email string `json:"email"`
// }import json
def json_to_go(data: dict, name: str = "Root") -> str:
lines = [f"type {name} struct {{"]
type_map = {str: "string", int: "int", float: "float64", bool: "bool"}
for key, value in data.items():
go_type = type_map.get(type(value), "interface{}")
field = key.title().replace("_", "") # snake_case → PascalCase
lines.append(f'\t{field} {go_type} `json:"{key}"`')
lines.append("}")
return "\n".join(lines)
data = json.loads('{"user_name": "Alice", "age": 30, "verified": true}')
print(json_to_go(data))
# type Root struct {
# UserName string `json:"user_name"`
# Age int `json:"age"`
# Verified bool `json:"verified"`
# }package main
import (
"encoding/json"
"fmt"
"strings"
)
type Event struct {
Type string `json:"type"`
Payload string `json:"payload"`
}
func main() {
stream := strings.NewReader(`{"type":"click","payload":"btn-1"}
{"type":"scroll","payload":"page-2"}`)
dec := json.NewDecoder(stream)
for dec.More() {
var ev Event
if err := dec.Decode(&ev); err != nil {
break
}
fmt.Printf("%s: %s\n", ev.Type, ev.Payload)
}
// → click: btn-1
// → scroll: page-2
}