英文:
Custom Marshaler for JSON that can be string or a map[string]string / map[string]bool
问题
我正在使用一个JSON响应进行工作,有时返回一个字符串,有时返回一个具有字符串键但值为字符串和布尔值的对象。我理解我需要为数据实现自己的Unmarshaler。
示例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{}:
// 如何处理值类型不同的其他 "isActive" 键?
*u = User{raw["id"].(string)}
}
return nil
}
这个问题/答案:这里 接近回答了我的用例,但我在如何处理多个不同值类型的映射值上有些困惑。
当前的Go Playground:这里
英文:
I am working with a JSON response that can sometimes return a string or an object with string keys but values that are string and bool. I understand I need to implement my own Unmarshaler for the data
Example JSON Situations:
caseOne := `"data": [
{"user": "usersName"}
]`
caseTwo := `"data": [
{"user": {"id": "usersId", "isActive": true}}
]`
My Code:
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
}
This question/answer: Here comes close to answering my use case but I am a bit stuck on how to handle multiple map values of different value types.
Current Go Playground:
Here
答案1
得分: 3
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}) { // 检查u是否只包含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 '"': // 字符串?
return json.Unmarshal(data, &u.Name)
case '{': // 对象?
type U User
return json.Unmarshal(data, (*U)(u))
}
return fmt.Errorf("不支持的JSON格式:%s", string(data))
}
如果你在MarshalJSON
方法内部直接将u
传递给json.Marshal
,或者在UnmarshalJSON
方法内部直接将u
传递给json.Unmarshal
,你的程序将陷入无限递归,并最终导致堆栈溢出,因为MarshalJSON/UnmarshalJSON
方法会被json.Marshal/json.Unmarshal
自动调用,而这些方法又会调用json.Marshal/json.Unmarshal
,从而形成无限循环。
为了避免这个问题,使用了类型U
。
语句type U User
声明了一个新类型U
,其底层类型与User
相同。由于底层类型相同,我们可以轻松地将一个类型转换为另一个类型,反之亦然。然而,类型声明语句并不会将旧类型的方法“继承”到新类型上,因此新类型U
不具有之前在User
上声明的任何方法,因此json.Marshal/json.Unmarshal
不会再陷入无限调用递归。
英文:
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
If you pass u
directly to json.Marshal
from inside MarshalJSON
, or if you pass it directly to json.Unmarshal
from inside UnmarshalJSON
, your program will get stuck in infinite recursion and eventually overflow the stack since MarshalJSON/UnmarshalJSON
are called automatically by json.Marshal/json.Unmarshal
on any value that implements those methods.
Type U
is used to avoid this problem.
The statement type U User
declares a new type U
with its underlying type identical to that of User
. Because the underlying types are identical we can easily convert one type to the other and back. However, the type declaration statement does not "carry over" the methods from the old type to the new type, so the new type U
has none of the methods previously declared on User
and therefore json.Marshal/json.Unmarshal
will not get stuck in infinite call recursion anymore.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论