我正在使用 JSON 响应,该响应有时可以返回字符串或带有字符串键但值为字符串和 bool 值的对象。我知道我需要为数据实现我自己的解码器
JSON 情况示例:
caseOne := `"data": [
{"user": "usersName"}
]`
caseTwo := `"data": [
{"user": {"id": "usersId", "isActive": true}}
]`
我的代码:
package main
type Result struct {
Data []Item `json:"data"`
}
type Item struct {
User User `json:"user"`
}
type User struct {
user string
}
func (u *User) MarshalJSON() ([]byte, error) {
return json.Marshal(u.user)
}
func (u *User) UnmarshalJSON(data []byte) error {
var raw interface{}
json.Unmarshal(data, &raw)
switch raw := raw.(type) {
case string:
*u = User{raw}
case map[string]interface{}:
// how do I handle the other "isActive" key that is map[string]bool?
*u = User{raw["id"].(string)}
}
return nil
}
此问题/答案:Here接近回答我的用例,但我对如何处理不同值类型的多个 map 值有点困惑。
当前围棋 Playground : Here
最佳答案
type User struct {
Id string `json:"id"`
Name string `json:"name"`
IsActive bool `json:"isActive"`
}
func (u User) MarshalJSON() ([]byte, error) {
if u == (User{Name: u.Name}) { // check if u contains only name
return json.Marshal(u.Name)
}
type U User
return json.Marshal(U(u))
}
func (u *User) UnmarshalJSON(data []byte) error {
switch data[0] {
case '"': // string?
return json.Unmarshal(data, &u.Name)
case '{': // object?
type U User
return json.Unmarshal(data, (*U)(u))
}
return fmt.Errorf("unsupported JSON: %s", string(data))
}
https://go.dev/play/p/toOIz0XOQUo
如果您从 MarshalJSON
内部将 u
直接传递给 json.Marshal
,或者直接将其传递给 json.Unmarshal
从 UnmarshalJSON
内部,您的程序将陷入无限递归并最终溢出堆栈,因为 MarshalJSON/UnmarshalJSON
由 json.Marshal/自动调用json.Unmarshal
对实现这些方法的任何值。
类型U
用于避免此问题。
语句type U User
声明了一个新类型U
,其基础类型与User
相同。因为底层类型是相同的,所以我们可以轻松地将一种类型转换为另一种类型并返回。但是,类型声明语句不会将旧类型的方法“继承”到新类型,因此新类型 U
没有先前在 User
上声明的方法> 因此 json.Marshal/json.Unmarshal
将不再陷入无限调用递归。
关于json - JSON 的自定义编码(marshal)拆收器,可以是字符串或 map[string]string/map[string]bool,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76007977/