我正在使用一个JSON响应,它有时可以返回字符串或具有字符串键但值为字符串和布尔值的对象.我知道我需要实现我自己的数据解组程序

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接近于回答我的用例,但我有点困惑于如何处理不同值类型的多个映射值.

目前的围棋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,或者如果您从UnmarshalJSON内部直接将其传递给json.Unmarshal,您的程序将陷入无限递归并最终溢出堆栈,因为json.Marshal/json.Unmarshal会在实现这些方法的任何值上自动调用MarshalJSON/UnmarshalJSON.

使用类型U可以避免此问题.

语句type U User声明其底层类型与User相同的新类型U.因为底层类型是相同的,所以我们可以很容易地将一种类型转换为另一种类型,然后再转换回来.然而,类型声明语句不会将方法从旧类型"继承"到新类型,因此新类型U没有先前在User上声明的任何方法,因此json.Marshal/json.Unmarshal将不再陷入无限调用递归.

Json相关问答推荐

使用单元和非单元版本反序列化Rust中的枚举,而无需编写自定义反序列化程序

在Snowflake中查询JSON时,属性名称是否支持绑定参数?

如何使用 JOLT 使用输入数组中的值和层次 struct 中的其他字段创建数组

如何使用jolt规范将一个对象添加到另一个对象中并删除该对象

JOLT 转换仅过滤一个字段

通过 xslt 将内部 json 转换为 xml 时遇到问题

PowerShell:如何将哈希表输出为 json 并使用 foreach 循环将其存储在变量中?

jq:用列表包装所有第一级原子值

JSON 模式实际用于什么目的?

在 rust 中从 API 反序列化 serde_json

使用 json_query 过滤嵌套列表中的元素

序列化特定类型时如何使 JSON.Net 序列化程序调用 ToString()?

Jackson Json:如何将数组转换为 JsonNode 和 ObjectNode?

TypeError: b'1' 不是 JSON 可序列化的

将循环 struct 转换为 JSON - 有什么方法可以找到它抱怨的字段?

我可以使用空字符串作为对象标识符吗?

仅使用字符串和值解析 JSON 对象

如何使用 SwiftyJSON 遍历 JSON?

JSON.stringify 向我的 Json 对象添加额外的 \ 和 "" 的问题

使用 JavaScriptSerializer() 反序列化 JSON 文件