我有一个ServiceAccount
类型,它嵌入了另外两种类型(用户和策略)。但是,由于User
类型的Unmarshaler实现,似乎完全忽略了Policy
类型的字段。
这种行为有什么好的理由吗?这看起来像是一个bug,因为json包可以通过反射看到我们嵌入了两种类型,而不仅仅是类型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
}
答案 0 :(得分:2)
我不认为这是一个错误,只是接口和嵌入的工作方式。它恰好不是你想要/期望的。
json.Unmarshal
计划通过this line使用UnmarshalJSON
方法:
if u, ok := v.Interface().(Unmarshaler); ok
如您所知,如果某个接口具有*Policy
和*ServiceAccount
所做的正确方法集,则会实现某个接口。因此,期望外部类型的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")}
但看起来真的 hacky对我来说。)
另见:
¹Further testing表明解码这样的匿名(即嵌入字段),他们的UnmarshalJSON
方法不会被调用。