我想执行一些其他步骤来初始化实现中的数据结构UnmarshalJSON。json.Unmarshal(b, type)在该实现内调用自然会导致堆栈溢出。
UnmarshalJSON
json.Unmarshal(b, type)
JSON解码器会不断尝试查找,如果有自定义UnmarshalJSON实现,然后再次调用json.Unmarshal。
json.Unmarshal
还有另一种方法吗?只是调用基本的默认实现而不会导致此问题?
避免这种情况/避免这种情况发生的一种简单且常见的方法是type使用关键字创建一个新类型,并使用类型转换来传递该类型的值(该值可能是您的原始值,因为新的类型具有原始类型作为其基础类型)。
type
之所以type可行,是因为关键字创建了一个新类型,并且该新类型将具有零个方法(它不会“继承”基础类型的方法)。
这会产生一些运行时开销吗?编号。从规格报价:转换:
特定规则适用于数字类型之间或字符串类型之间的(非恒定)转换。这些转换可能会更改的表示形式x并产生运行时成本。 所有其他转换只会更改类型,而不会更改的表示形式x。
x
让我们来看一个例子。我们有一个Person带有数字的类型Age,我们要确保Age不能为负数(小于0)。
Person
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()):
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()string为fmt包定义自定义文本表示方法时,您想使用所修改的默认字符串表示形式。
String()string
fmt