golang的encoding/json通过结构体标签(struct tags)实现复杂json结构的优雅处理。①字段映射:使用json:"tag"将json字段名与go结构体字段名不一致的情况进行绑定,如json:"user_id"对应go字段id;②忽略字段:通过json:"-"标签使字段在序列化和反序列化时被忽略;③可选字段与空值控制:omitempty标签用于在序列化时省略零值字段,结合指针类型(如*string)区分“字段不存在”与“字段为null”;④嵌套结构体支持:定义多层嵌套结构体以匹配深层json结构,确保类型安全且代码清晰;⑤动态json处理:使用interface{}或map[string]interface{}应对不确定结构,或通过json.rawmessage与自定义unmarshaler接口实现灵活解析,兼顾类型安全与扩展性。

encoding/json

要用Golang的
encoding/json
json:"tag"

基本用法与字段重命名: JSON字段名和Go结构体字段名不一致时,这是最常用的场景。
type User struct {
ID string `json:"user_id"` // JSON中的user_id 映射到 Go的ID
Name string `json:"name"`
Email string `json:"email_address"` // JSON中的email_address 映射到 Go的Email
}
// 对应的JSON可能长这样:
// {
// "user_id": "123",
// "name": "Alice",
// "email_address": "alice@example.com"
// }忽略字段: 如果你想在JSON编码或解码时完全忽略某个字段,可以使用
json:"-"

type Product struct {
Name string `json:"name"`
Price float64 `json:"price"`
InternalSKU string `json:"-"` // 这个字段不会出现在JSON中,也不会从JSON中解析
}可选字段与空值处理:omitempty
omitempty
立即学习“go语言免费学习笔记(深入)”;
type Order struct {
OrderID string `json:"order_id"`
CustomerID string `json:"customer_id"`
TotalAmount float64 `json:"total_amount"`
CouponCode *string `json:"coupon_code,omitempty"` // 如果CouponCode为nil或空字符串,则在JSON中省略
IsPaid bool `json:"is_paid,omitempty"` // 如果IsPaid为false,则在JSON中省略
}
// 编码时:
// order1 := Order{OrderID: "A1", CustomerID: "C1", TotalAmount: 100.0} // CouponCode和IsPaid会省略
// order2 := Order{OrderID: "A2", CustomerID: "C2", TotalAmount: 200.0, IsPaid: true} // IsPaid会包含对于解码,如果JSON字段不存在或为
null
*string
Unmarshaler
嵌套结构体: 对于复杂的、多层嵌套的JSON,直接在Go中定义对应的嵌套结构体即可。
type Address struct {
Street string `json:"street"`
City string `json:"city"`
ZipCode string `json:"zip_code"`
}
type Customer struct {
ID string `json:"id"`
Name string `json:"name"`
BillingAddress Address `json:"billing_address"` // 嵌套结构体
ShippingAddress *Address `json:"shipping_address,omitempty"` // 可选的嵌套结构体
}
// 对应的JSON:
// {
// "id": "cust123",
// "name": "Bob",
// "billing_address": {
// "street": "123 Main St",
// "city": "Anytown",
// "zip_code": "12345"
// },
// "shipping_address": null // 或者完全没有这个字段
// }字段名不一致确实是个老问题,尤其是在对接第三方API时,他们的命名规范可能和你的Go代码风格完全不同。这时候,
json:"actual_name"
// 假设JSON长这样:
// {
// "product_details": {
// "item_id": "P001",
// "item_name": "Laptop Pro",
// "manufacturer_info": {
// "company_name": "Tech Corp",
// "country_of_origin": "USA"
// }
// }
// }
type ManufacturerInfo struct {
CompanyName string `json:"company_name"`
CountryOfOrigin string `json:"country_of_origin"`
}
type ProductDetails struct {
ItemID string `json:"item_id"`
ItemName string `json:"item_name"`
ManufacturerInfo ManufacturerInfo `json:"manufacturer_info"`
}
type Response struct {
ProductDetails ProductDetails `json:"product_details"`
}你看,即使JSON里头有
product_details
item_id
manufacturer_info
Response
ProductDetails
ManufacturerInfo
map[string]interface{}有时候,JSON的嵌套层级可能会深到让你觉得有点“过分”,比如一个
data
details
details
attributes
type X struct { Y Z }encoding/json
处理空值和可选字段,这在API交互里头简直是家常便饭。一个字段可能有时出现,有时不出现;或者有时是具体的值,有时是
null
encoding/json
Easily find JSON paths within JSON objects using our intuitive Json Path Finder
30
最直接且推荐的方式是使用指针类型。当JSON中的某个字段可能不存在或为
null
*string
*int
*bool
null
nil
type UserProfile struct {
Name string `json:"name"`
Age *int `json:"age,omitempty"` // 如果JSON中age为null或不存在,则Age为nil
Bio *string `json:"bio,omitempty"` // 如果JSON中bio为null或不存在,则Bio为nil
IsActive *bool `json:"is_active,omitempty"` // 如果JSON中is_active为null或不存在,则IsActive为nil
}
// 假设JSON:
// { "name": "Alice", "age": null } -> Age: nil
// { "name": "Bob" } -> Age: nil
// { "name": "Charlie", "age": 30 } -> Age: &30omitempty
nil
然而,如果你需要更细致的控制,比如一个字段在JSON中可能是一个字符串,也可能是一个数字,甚至可能是一个对象,或者你对
null
null
nil
json.Unmarshaler
json.Marshaler
通过实现这两个接口,你可以完全自定义字段的序列化和反序列化过程。这给了你最大的灵活性,但同时也增加了代码的复杂性。比如,一个字段可能既可以是字符串
"N/A"
null
type NullableString string
func (ns *NullableString) UnmarshalJSON(data []byte) error {
if string(data) == "null" || string(data) == `""` || string(data) == `"N/A"` {
*ns = "" // 将null、空字符串或"N/A"都映射为空字符串
return nil
}
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
*ns = NullableString(s)
return nil
}
// 假设JSON: { "description": "N/A" } 或 { "description": null } 或 { "description": "" }
type Item struct {
Name string `json:"name"`
Description NullableString `json:"description"`
}这种自定义方式虽然强大,但通常只在标准标签和指针无法满足需求时才考虑使用,因为它会增加代码量和理解成本。对我来说,能用标签解决的,绝不轻易上自定义接口。
当JSON结构不是固定不变,而是会根据某些条件动态变化时,
encoding/json
data
使用interface{}map[string]interface{}
interface{}map[string]interface{}type DynamicResponse struct {
Status string `json:"status"`
Data interface{} `json:"data"` // Data字段可以是任何类型
}
// 假设JSON 1: { "status": "success", "data": "hello world" }
// 假设JSON 2: { "status": "success", "data": { "id": 1, "name": "Test" } }
// 假设JSON 3: { "status": "success", "data": [1, 2, 3] }
// 解码后,你需要对Data字段进行类型断言:
// var resp DynamicResponse
// err := json.Unmarshal(jsonData, &resp)
// if err != nil { /* handle error */ }
//
// switch v := resp.Data.(type) {
// case string:
// fmt.Println("Data is a string:", v)
// case map[string]interface{}:
// fmt.Println("Data is an object:", v["name"])
// case []interface{}:
// fmt.Println("Data is an array:", v[0])
// default:
// fmt.Println("Unknown data type")
// }这种方式的缺点是牺牲了类型安全。你需要在运行时进行类型断言,这增加了出错的可能性,并且代码可读性也会下降。它更像是Go在面对“我不知道会来什么”时的“应急通道”。
结合json.RawMessage
Unmarshaler
json.RawMessage
Unmarshaler
json.RawMessage
UnmarshalJSON
RawMessage
type Event struct {
EventType string `json:"event_type"`
Payload json.RawMessage `json:"payload"` // 原始的JSON数据
}
type UserCreatedPayload struct {
UserID string `json:"user_id"`
UserName string `json:"user_name"`
}
type OrderPlacedPayload struct {
OrderID string `json:"order_id"`
Amount float64 `json:"amount"`
}
// 自定义 UnmarshalJSON 方法
func (e *Event) UnmarshalJSON(data []byte) error {
// 先解析一个临时结构体,只获取 EventType
type Alias Event
aux := &struct {
*Alias
}{
Alias: (*Alias)(e),
}
if err := json.Unmarshal(data, aux); err != nil {
return err
}
// 根据 EventType 决定如何解析 Payload
switch e.EventType {
case "user_created":
var p UserCreatedPayload
if err := json.Unmarshal(e.Payload, &p); err != nil {
return err
}
// 这里你可能需要一个 interface{} 字段来存储具体解析后的Payload
// 比如在Event结构体里加一个 `ParsedPayload interface{}`
// e.ParsedPayload = p
case "order_placed":
var p OrderPlacedPayload
if err := json.Unmarshal(e.Payload, &p); err != nil {
return err
}
// e.ParsedPayload = p
default:
// 处理未知类型或者直接保留RawMessage
}
return nil
}这种方式非常强大,但复杂性也最高。它适用于那些需要严格类型安全,并且动态结构有明确规则可循的场景。我个人觉得,如果你的JSON结构动态性到了这个地步,那设计层面可能需要考虑一下是不是可以在源头就进行规范化,或者考虑使用像Protocol Buffers或gRPC这种强类型的数据传输协议,而不是JSON。但如果必须用JSON,并且要保证类型安全,那
RawMessage
Unmarshaler
以上就是如何用Golang的encoding/json处理复杂JSON 分享结构体标签技巧的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号