英文:
In Go, why is JSON null sometimes not passed to UnmarshalJSON for decoding?
问题
Go提供了encoding/json.Unmarshaler接口,以便类型可以控制它们从JSON解码的方式。在几乎所有情况下,编码的JSON值直接传递给UnmarshalJSON方法,但如果Unmarshaler是一个指针并且JSON值为null,则不会调用UnmarshalJSON,而是将指针设置为nil。这里有一个例子:
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 %#v\n", a.T)
if a.PT1 == nil {
fmt.Println("a.PT1 is nil")
} else {
fmt.Printf("a.PT1 points to %#v\n", *a.PT1)
}
if a.PT2 == nil {
fmt.Println("a.PT2 is nil")
} else {
fmt.Printf("a.PT2 points to %#v\n", *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。为什么会这样呢?
英文:
Go provides the encoding/json.Unmarshaler interface so types can control the way they are decoded from JSON. In almost all cases, the encoded JSON value is passed directly to the UnmarshalJSON method, but not if the Unmarshaler is a pointer and the JSON value is null. In that case, the pointer is set to nil without calling UnmarshalJSON at all. Here's an example:
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 %#v\n", a.T)
if a.PT1 == nil {
fmt.Println("a.PT1 is nil")
} else {
fmt.Printf("a.PT1 points to %#v\n", *a.PT1)
}
if a.PT2 == nil {
fmt.Println("a.PT2 is nil")
} else {
fmt.Printf("a.PT2 points to %#v\n", *a.PT2)
}
}
I expected this to print
<!-- language: lang-none -->
a.T is "null"
a.PT1 points to "not null"
a.PT2 points to "null"
Instead, it prints
<!-- language: lang-none -->
a.T is "null"
a.PT1 points to "not null"
a.PT2 is nil
So json.Unmarshal allocates a new T for a.PT1, which is initially nil. But it sets a.PT2 to nil without calling UnmarshalJSON, even though a.PT2 was not nil. Why?
答案1
得分: 1
这是因为将指针设置为nil是处理JSON中的null最常见的方式,而*T类型的UnmarshalJSON方法本身无法实现这一点。如果在这种情况下调用UnmarshalJSON,你将不得不定义(**T).UnmarshalJSON来将*T设置为nil。这将使最常见的情况变得非常笨拙。
如果你不希望JSON中的null变成Go中的nil,就不要使用指针。
英文:
This is because setting the pointer to nil is the most common way to handle a JSON null, and there is no way for an UnmarshalJSON method of *T to do that on its own. If UnmarshalJSON were called in this case, you would have to define (**T).UnmarshalJSON to set the *T to nil. This would make the most common case very awkward.
If you don't want JSON null to become Go nil, don't use a pointer.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论