具有多个嵌入式结构的 Go MarshalJSON 行为

Go MarshalJSON behavior with multiple embedded structs

我正在测试带有嵌入式结构的 go json 封送处理。但是,我看到当我嵌入 time.Time 时,为什么它总是覆盖其他嵌入的结构,即使它们也提供了自己的自定义封送处理?下面的代码总是打印出 "0001-01-01T00:00:00Z"

package main

import (
    "encoding/json"
    "fmt"
    "time"
)

type A struct {
}

func (a A) Print() {
    fmt.Println("A")
}

type B struct {
    B int
}

func (a A) MarshalJSON() ([]byte, error) {
    return []byte(`"a"`), nil
}

func (b B) MarshalJSON() ([]byte, error) {
    return []byte(`"b"`), nil
}

func (a B) Print() {
    fmt.Println("A")
}

type C struct {
    A
    B
    time.Time
    C int `json:"C"`
}

func main() {
    fmt.Println("Hello, 世界")
    c := C{}
    decode, err := json.Marshal(c)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(string(decode))
}

如果您嵌入多个具有相同名称的字段或方法的类型,那么将无法再直接访问这些字段或方法。

Selectors:

x.f

  1. For a value x of type T or *T where T is not a pointer or interface type, x.f denotes the field or method at the shallowest depth in T where there is such an f. If there is not exactly one f with shallowest depth, the selector expression is illegal.

这意味着,给定以下类型集:

type S1 struct { F string }

type S2 struct { F string }

type S3 struct {
    S1
    S2
}

表达式s3.F不合法:

var s3 S3
_ = s3.F // ambiguous selector s3.F

这是因为在最浅的深度有多个F。您可以在 playground.

上试用

相同的规则适用于方法。由此可见,您的类型 C 不满足 json.Marshaler 接口 因为 它在相同的深度嵌入了 更多 =68=] 实现 MarshalJSON() 方法的类型。您可以在 playground.

上亲自查看

然而,问题仍然存在,即为什么无论如何都使用嵌入式 time.Time 的自定义封送处理。这是因为 time.Time 不仅实现了 json.Marshaler 接口,还实现了 encoding.TextMarshaler 接口(docs & playground). And the json.Marshal's documentation 表示如下:

Marshal traverses the value v recursively. If an encountered value implements the Marshaler interface and is not a nil pointer, Marshal calls its MarshalJSON method to produce JSON. If no MarshalJSON method is present but the value implements encoding.TextMarshaler instead, Marshal calls its MarshalText method and encodes the result as a JSON string.

一旦您还AB实现了encoding.TextMarshaler接口,您就可以see here上述行为成立,然后time.Time' s MarshalText 方法将不再使用。