在 UnmarshalJSON 函数内调用 json.Unmarshal 不会导致堆栈溢出 [英] Call json.Unmarshal inside UnmarshalJSON function without causing stack overflow

查看:40
本文介绍了在 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.

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

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?

推荐答案

避免这种情况/保护的一种简单而常用的方法是使用 type 关键字,并使用类型 转换 传递这个类型的值(该值可能是你的原始值,类型转换是可能的,因为新类型将原始类型作为其底层类型).

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: Conversions:

特定规则适用于数字类型之间或字符串类型之间的(非常量)转换.这些转换可能会改变 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)

输出(在 Go Playground 上试试):

Output (try it on the Go Playground):

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

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

Output (on the same Go Playground example):

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

一个非常相似的问题是当您为 String() string 方法时"noreferrer">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天全站免登陆