当有 Unmarshal 实现时,为什么不解码嵌入式结构?

我有一个ServiceAccount嵌入其他两种类型(用户和策略)的类型。然而,User由于该Policy类型的 Unmarshaler 实现,该类型的字段似乎被完全忽略了。这种行为有什么好的理由吗?对我来说这看起来像是一个错误,因为 json 包可以通过反射看到我们嵌入了两种类型,而不仅仅是 type Policy。


我知道我也可以通过在 ServiceAccount 类型上实现 Unmarshaler 接口来“修复”这个问题。


package main


import (

    "encoding/json"

    "fmt"


type ServiceAccount struct {

    User

    Policy

}

type User struct {

    UserID string `json:"userID"`

}


type Policy struct {

    Scopes string `json:"scopes,omitempty"`

}


// PolicyRaw is the Policy type as received from client.

type PolicyRaw struct {

    Scopes string `json:"scopes,omitempty"`

}


func main() {

    s := `{"userID":"xyz", "scopes":"some scopes"}`


    srvAcc := &ServiceAccount{}

    if err := json.Unmarshal([]byte(s), srvAcc); err != nil {

        panic(err)

    }

    fmt.Printf("srvAcc %v", *srvAcc)

}


func (p *Policy) UnmarshalJSON(b []byte) error {

    pr := new(PolicyRaw)

    if err := json.Unmarshal(b, pr); err != nil {

        return err

    }

    p.Scopes = pr.Scopes

    return nil

}


守着一只汪
浏览 175回答 1
1回答

潇湘沐

我不认为这是一个错误,而只是接口和嵌入的工作方式。它只是碰巧不是您想要/期望的。json.Unmarshal弄清楚UnmarshalJSON通过这一行使用该方法:    if u, ok := v.Interface().(Unmarshaler); ok如您所知,如果某个接口具有正确的方法集 which*Policy和*ServiceAccountdo,则它会实现接口。因此,预计外部类型的 JSON 解码只会调用适当的方法并认为它已完成。有趣的是,如果您要进行试验并添加一个虚拟方法,例如:func (u *User) UnmarshalJSON([]byte) error {return errors.New("not impl")}然后虽然*User和*Policy现在都实现了接口,但 *ServiceAccount将不再实现该接口。原因很明显,如果您尝试并显式调用srvAcc.UnmarshalJSON它,则会给出“模棱两可的选择器 srvAcc.UnmarshalJSON ”的编译器错误。如果没有这样的调用,代码是合法的,并且该方法只是从方法集中排除。所以我认为解决方案是以下之一:json.Unmarshaller如果您想编组结果,请不要嵌入实现的东西,例如使用命名字段。json.Unmarshaller在进行此类嵌入时,请确保您自己为外部类型显式实现(例如,向 中添加UnmarshalJSON方法*ServiceAccount)。确保至少实现了两个嵌入的东西json.Unmarshaller,然后它们将单独工作¹,但“外部”类型将获得默认行为。最后一个选项对我来说似乎是一个黑客。(顺便说一句,可以故意这样做: type ServiceAccount struct {        User        Policy        dummyMarshaller    }    type dummyMarshaller struct{}    func (dummyMarshaller) MarshalJSON([]byte) error {panic("ouch")}但这对我来说看起来真的很糟糕)。也可以看看:https://golang.org/ref/spec#Struct_typeshttps://golang.org/doc/effective_go.html#embedding¹进一步的测试表明,解码此类匿名(即嵌入字段),它们的UnmarshalJSON方法不会被调用。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go