json - JSON 的自定义编码(marshal)拆收器,可以是字符串或 map[string]string/map[string]bool

标签 json go marshalling

我正在使用 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/UnmarshalJSONjson.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/

相关文章:

Go - 从 ISO 年周开始的一周第一天的 Unix 时间戳

mysql - 如何在 Go 中构建类型接口(interface)的变量?

json - 相当于Python的json.dumps和json.loads

java - 使用 Hibernate 将嵌套的 JSON 数据保存到 MySQL 数据库中

c# - 只有一个对象时将 XML 转换为 Json 数组

javascript - 访问 Javascript 变量中的 JSON 对象

javascript - 将图像作为正文附加到 golang 服务器的 POST 请求中

C# P/调用 : Marshalling structures containing function pointers

java - 如何使用 xstream 将 xml 字符串附加到现有的 xml 文件

javascript - 从 HTML 脚本向另一个域中的 Node.JS 应用程序发出 POST JSON 请求