在 Go 中,为什么 JSON null 有时不传递给 UnmarshalJSON 进行解码



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.Unmarshala.PT1分配了一个新的T,最初是nil。但它将a.PT2设置为nil而不调用UnmarshalJSON,即使a.PT2没有nil。为什么?

这是因为将指针设置为nil是处理 JSON null的最常见方法,而UnmarshalJSON *T方法无法自行执行此操作。如果在这种情况下调用UnmarshalJSON,则必须定义(**T).UnmarshalJSON才能将*T设置为 nil 。这将使最常见的情况变得非常尴尬。

如果您不希望 JSON null变成 Go nil,请不要使用指针。

最新更新