Go 提供了encoding/json.Unmarshaler
接口,因此类型可以控制它们从 JSON 解码的方式。在几乎所有情况下,编码的 JSON 值都会直接传递给 UnmarshalJSON
方法,但如果Unmarshaler
是指针并且 JSON 值null
,则不会。在这种情况下,指针设置为nil
而不调用UnmarshalJSON
。下面是一个示例:
package main
import (
"encoding/json"
"fmt"
)
type T string
func (v *T) UnmarshalJSON(b []byte) error {
if b[0] == 'n' {
*v = "null"
} else {
*v = "not null"
}
return nil
}
func main() {
var a struct {
T T
PT1 *T
PT2 *T
}
a.PT1 = nil // just to be explicit
a.PT2 = new(T)
err := json.Unmarshal([]byte(`{"T":null,"PT1":"foo","PT2":null}`), &a)
if err != nil {
panic(err)
}
fmt.Printf("a.T is %#vn", a.T)
if a.PT1 == nil {
fmt.Println("a.PT1 is nil")
} else {
fmt.Printf("a.PT1 points to %#vn", *a.PT1)
}
if a.PT2 == nil {
fmt.Println("a.PT2 is nil")
} else {
fmt.Printf("a.PT2 points to %#vn", *a.PT2)
}
}
我希望这会打印
a.T is "null"
a.PT1 points to "not null"
a.PT2 points to "null"
相反,它会打印
a.T is "null"
a.PT1 points to "not null"
a.PT2 is nil
所以json.Unmarshal
为a.PT1
分配了一个新的T
,最初是nil
。但它将a.PT2
设置为nil
而不调用UnmarshalJSON
,即使a.PT2
没有nil
。为什么?
这是因为将指针设置为nil
是处理 JSON null
的最常见方法,而UnmarshalJSON
*T
方法无法自行执行此操作。如果在这种情况下调用UnmarshalJSON
,则必须定义(**T).UnmarshalJSON
才能将*T
设置为 nil
。这将使最常见的情况变得非常尴尬。
如果您不希望 JSON null
变成 Go nil
,请不要使用指针。