在UnmarshalJSON函数中调用json.Unmarshal而不引起堆栈溢出 [英] Call json.Unmarshal inside UnmarshalJSON function without causing stack overflow

查看:104
本文介绍了在UnmarshalJSON函数中调用json.Unmarshal而不引起堆栈溢出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I wanted to perform some additional steps for initializing a data structure inside my implementation UnmarshalJSON. Calling json.Unmarshal(b, type) inside that implementation, naturally, causes a stack overflow.

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

The JSON decoder is continiously trying to look up, if there is a custom UnmarshalJSON implementation which then again, calls json.Unmarshal.

还有另一种方法吗?只是调用基本的默认实现而不会导致这种情况?

Is there another way to do this? Just call the underlying default implementation without causing this?

推荐答案

一种避免这种情况/避免这种情况发生的简单且常见的方法是使用转换以传递此类型的值(该值可能是您的原始值,因为新类型具有原始类型作为其基础类型,所以可以进行类型转换).

An easy and common way to avoid this / protect from it is to create a new type with the type keyword, and use a type conversion to pass a value of this type (the value may be your original value, type conversion is possible because the new type has the original type as its underlying type).

之所以可行,是因为type关键字创建了一个新类型,并且该新类型将具有零个方法(它不会继承"基础类型的方法).

This works because the type keyword creates a new type, and the new type will have zero methods (it does not "inherit" the methods of the underlying type).

这会产生一些运行时开销吗?否.引用自规范:类型声明:

Does this incur some run-time overhead? No. Quoting from Spec: Type declarations:

特定规则适用于数字类型之间或字符串类型之间的(非恒定)转换.这些转换可能会更改x的表示形式,并产生运行时成本. 所有其他转换只会更改类型,而不会更改x的表示形式.

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).

Let's see an example. We have a Person type with a numeric Age, and we want to make sure the Age cannot be negative (less than 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)

输出(在转到游乐场上尝试):

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

当然,相同的技术也适用于自定义封送(MarshalJSON()):

Of course the same technique works for custom marshaling (MarshalJSON()) too:

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))

输出(在相同的转到操场示例中):

Output (on the same Go Playground example):

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

一个非常相似的问题是,当您为 fmt 包,并要使用您修改的默认字符串表示形式.在此处了解更多信息: t和* t之间的区别

A very similar issue is when you define the String() string method for custom text representation for the fmt package, and you want to use the default string representation which you modify. Read more about it here: The difference between t and *t

这篇关于在UnmarshalJSON函数中调用json.Unmarshal而不引起堆栈溢出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆