调用 json.在 UnmarshalJSON 函数中取消封送,而不会导致堆栈溢出



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

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

有没有其他方法可以做到这一点?只需调用底层默认实现而不导致此问题?

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

这是因为type关键字创建一个新类型,并且新类型将具有零个方法(它不会"继承"基础类型的方法)。

这是否会产生一些运行时开销?不。引用自规范: 转换:

特定规则适用于数值类型之间的(非常量)转换或字符串类型之间的(非常量)转换。这些转换可能会更改x的表示形式并产生运行时成本。所有其他转换仅更改类型,而不会更改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}

当然,同样的技术也适用于自定义封送处理(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方法时,并且您希望使用您修改的默认字符串表示形式。在这里阅读更多关于它的信息:t和*t之间的区别

最新更新