dougui1977 2015-03-11 00:20
浏览 24
已采纳

为什么在执行Unmarshal实现时未对嵌入式结构进行解码?

I have a type ServiceAccount which embeds two other types (User and Policy). However it seems the fields of the User type are totally ignored due the Unmarshaler implementation of the Policy type. Is there any good reason for this behaviour? It looks like a bug to me because the json package can see through reflection that we have two types embedded and not only the type Policy.

I'm aware that I can "fix" the issue by implementing the Unmarshaler interface on type ServiceAccount too.

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
}

Execute

  • 写回答

1条回答 默认 最新

  • dpli36193 2015-03-11 01:27
    关注

    I don't think it's a bug but just just how interfaces and embedding works. It just happens not to be what you want/expect here.

    json.Unmarshal figures out to use the UnmarshalJSON method via this line:

        if u, ok := v.Interface().(Unmarshaler); ok
    

    As you know, something implements an interface if it has the right method set which *Policy and *ServiceAccount do. So it's expected that JSON decoding of the the outer type would just call the appropriate method and think it's done.

    Interestingly, if you were to experiment and add a dummy method such as:

    func (u *User) UnmarshalJSON([]byte) error {return errors.New("not impl")}
    

    then although *User and *Policy would now both implement the interface, *ServiceAccount will no longer implement that interface. The reason is clear if you try and explicitly call srvAcc.UnmarshalJSON which would then give a compiler error of "ambiguous selector srvAcc.UnmarshalJSON". Without such a call the code is legal and the method is just excluded from the method set.

    So I think the solution is one of:

    • Just don't embed things that implement json.Unmarshaller if you want to marshal the result, e.g. use a named field instead.
    • Make sure you explicitly implement json.Unmarshaller for the outer type yourself when doing such embedding (e.g. add an UnmarshalJSON method to *ServiceAccount).
    • Make sure at least two embedded things implement json.Unmarshaller and then they'll work individually¹ but the "outer" type will get the default behaviour.

    The last option seems like a hack to me. (And btw could be done on purpose with something like:

        type ServiceAccount struct {
            User
            Policy
            dummyMarshaller
        }
    
        type dummyMarshaller struct{}
        func (dummyMarshaller) MarshalJSON([]byte) error {panic("ouch")}
    

    but that looks really hacky to me).

    See also:

    ¹ Further testing shows that decoding such anonymous (i.e. embedded fields), that their UnmarshalJSON methods do not get called.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 对于相关问题的求解与代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 信号傅里叶变换在matlab上遇到的小问题请求帮助
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作
  • ¥15 求NPF226060磁芯的详细资料