为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
守着一只汪 2021-09-20 20:38:33
我有一个ServiceAccount嵌入其他两种类型(用户和策略)的类型。然而,User由于该Policy类型的 Unmarshaler 实现,该类型的字段似乎被完全忽略了。这种行为有什么好的理由吗?对我来说这看起来像是一个错误,因为 json 包可以通过反射看到我们嵌入了两种类型,而不仅仅是 type Policy。我知道我也可以通过在 ServiceAccount 类型上实现 Unmarshaler 接口来“修复”这个问题。package mainimport (    "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}
查看完整描述

1 回答

?
潇湘沐

TA贡献1816条经验 获得超6个赞

我不认为这是一个错误,而只是接口和嵌入的工作方式。它只是碰巧不是您想要/期望的。

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_types

https://golang.org/doc/effective_go.html#embedding

¹进一步的测试表明,解码此类匿名(即嵌入字段),它们的UnmarshalJSON方法不会被调用。


查看完整回答
反对 回复 2021-09-20
  • 1 回答
  • 0 关注
  • 173 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信