当前位置: 首页 > 面试题库 >

在UnmarshalJSON函数中调用json.Unmarshal而不引起堆栈溢出

贺飞
2023-03-14
问题内容

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

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

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


问题答案:

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

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

这会产生一些运行时开销吗?编号。从规格报价:转换:

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

让我们来看一个例子。我们有一个Person带有数字的类型Age,我们要确保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>

一个非常相似的问题是,当您String()stringfmt包定义自定义文本表示方法时,您想使用所修改的默认字符串表示形式。



 类似资料:
  • 问题内容: 这有效:http : //play.golang.org/p/-Kv3xAguDR。 这导致堆栈溢出:http : //play.golang.org/p/1-AsHFj51O。 我不明白为什么。在这种情况下,使用接口的正确方法是什么? 问题答案: 这个 将呼叫您的,依次呼叫,等等。如果您需要解组JSON然后对其进行处理,那么一种巧妙的技术是声明一个本地类型,将数据解组到其中,然后转换

  • 我有一个执行快速排序的应用程序。在我开始给它一些更大的数字(我第一次得到它是10000000)之前,它工作得很好。我知道是由递归引起的,但我不明白为什么我的应用程序会因此而崩溃。如有任何建议,将不胜感激。这是我的密码:

  • 函数堆栈 栈是一个很重要的编程概念(编译课和程序设计课都讲过相关内容),与编译器和编程语言有紧密的联系。理解调用栈最重要的两点是:栈的结构,EBP寄存器的作用。一个函数调用动作可分解为:零到多个PUSH指令(用于参数入栈),一个CALL指令。CALL指令内部其实还暗含了一个将返回地址(即CALL指令下一条指令的地址)压栈的动作(由硬件完成)。几乎所有本地编译器都会在每个函数体之前插入类似如下的汇编

  • 问题内容: 下面给出的代码显示了运行时的Stackoverflow错误。但是,如果我使另一个类CarChange创建Car的对象,它将成功运行。我是一个初学者,请执行以下代码以了解在Java中进行向上转换的重要性。 问题答案: 一个stackoverflow通常意味着您有一个无限循环。 收到此消息的原因是因为您从testdrive方法调用驱动器,并且在该方法中再次调用drive。

  • 问题内容: 因此,我有主类在运行时调用。在Secondary类中,在顶部的代码是。 如何在不引起堆栈溢出错误的情况下使用次要类中的所有方法和变量,反之亦然? 注意:它们不在构造函数中 问题答案: 您的Main类正在创建一个Secondary实例,在创建一个Main实例…,这会导致堆栈溢出错误。 我认为您只是希望对象之间能够相互引用,所以不要在构造函数中创建另一个类的新实例。将引用声明为实例变量,并