英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论