json - 在 UnmarshalJSON 函数中调用 json.Unmarshal 不会导致堆栈溢出

标签 json serialization go

我想执行一些额外的步骤来初始化我的实现 UnmarshalJSON 中的数据结构。在该实现中调用 json.Unmarshal(b, type) 自然会导致堆栈溢出。

JSON 解码器不断尝试查找,如果有自定义的 UnmarshalJSON 实现,然后再次调用 json.Unmarshal

还有其他方法吗?只需调用底层默认实现而不会导致这种情况?

最佳答案

避免这种情况/防止这种情况发生的一种简单而常用的方法是使用 type 创建一个新类型关键字,并使用类型 conversion传递此类型的值(该值可能是您的原始值,类型转换是可能的,因为新类型将原始类型作为其基础类型)。

这是有效的,因为 type 关键字创建了一个新类型,而新类型将有零个方法(它不会“继承”底层类型的方法)。

这会产生一些运行时开销吗?编号引用自Spec: Conversions:

Specific rules apply to (non-constant) conversions between numeric types or to and from a string type. These conversions may change the representation of x and incur a run-time cost. All other conversions only change the type but not the representation of x.

让我们看一个例子。我们有一个带有数字 AgePerson 类型,我们要确保 Age 不能为负数(小于 0)。

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func (p *Person) UnmarshalJSON(data []byte) error {
    type person2 Person
    if err := json.Unmarshal(data, (*person2)(p)); err != nil {
        return err
    }

    // Post-processing after unmarshaling:
    if p.Age < 0 {
        p.Age = 0
    }
    return nil
}

测试它:

var p *Person
fmt.Println(json.Unmarshal([]byte(`{"name":"Bob","age":10}`), &p))
fmt.Println(p)

fmt.Println(json.Unmarshal([]byte(`{"name":"Bob","age":-1}`), &p))
fmt.Println(p)

输出(在 Go Playground 上尝试):

<nil>
&{Bob 10}
<nil>
&{Bob 0}

当然,同样的技术也适用于自定义编码(marshal)处理 (MarshalJSON()):

func (p *Person) MarshalJSON() ([]byte, error) {
    // Pre-processing before marshaling:
    if p.Age < 0 {
        p.Age = 0
    }

    type person2 Person
    return json.Marshal((*person2)(p))
}

测试它:

p = &Person{"Bob", 10}
fmt.Println(json.NewEncoder(os.Stdout).Encode(p))
p = &Person{"Bob", -1}
fmt.Println(json.NewEncoder(os.Stdout).Encode(p))

输出(在同一个 Go Playground 示例中):

{"name":"Bob","age":10}
<nil>
{"name":"Bob","age":0}
<nil>

一个非常相似的问题是当您为 fmt 的自定义文本表示定义 String() string 方法时包,并且您想使用您修改的默认字符串表示形式。在这里阅读更多相关信息:The difference between t and *t

关于json - 在 UnmarshalJSON 函数中调用 json.Unmarshal 不会导致堆栈溢出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43176625/

相关文章:

c# - 使用 JSON.NET 将属性反序列化为 ExpandoObject

c# - protobuf-net - 为什么反序列化对象后引用同一对象不相等

serialization - Dart 序列化不可变对象(immutable对象)

linux - 无法安装 Sys Go 包,我尝试了正常的方式,但它没有意义。请在下面找到我的 Go env

json - 无法解码golang回应

json - Go lang 解码 io.read JSON 与 JSON解码给出不同的结果

python - 如何使用 python 动态展平深度嵌套的 json 文件?

php - 检查用户名是否存在于Android代码中

c# - 分组为数组

javascript - 在react js中访问json文件