doubiao7410 2016-07-20 19:59
浏览 92

使用自定义MarshalJSON()方法嵌入结构的惯用方式

Given the following structs:

type Person {
    Name string `json:"name"`
}

type Employee {
    Person
    JobRole string `json:"jobRole"`
}

I can easily marshal an Employee to JSON as expected:

p := Person{"Bob"}
e := Employee{&p, "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s
", string(output))

Output:

{"name":"Bob","jobRole":"Sales"}

But when the embedded struct has a custom MarshalJSON() method...

func (p *Person) MarshalJSON() ([]byte,error) {
    return json.Marshal(struct{
        Name string `json:"name"`
    }{
        Name: strings.ToUpper(p.Name),
    })
}

it breaks entirely:

p := Person{"Bob"}
e := Employee{&p, "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s
", string(output))

Now results in:

{"name":"BOB"}

(Note the conspicuous lack of jobRole field)

This is easily anticipated... the embedded Person struct implements the MarshalJSON() function, which is being called.

The trouble is, it's not what I want. What I want would be:

{"name":"BOB","jobRole":"Sales"}

That is, encode Employee's fields normally, and defer to Person's MarshalJSON() method to marshal its fields, and hand back some tidy JSON.

Now I could add a MarshalJSON() method to Employee as well, but this requires that I know that the embedded type implements MarshalJSON() as well, and either (a) duplicate its logic, or (b) call Person's MarshalJSON() and somehow manipulate its output to fit where I want it. Either approach seems sloppy, and not very future proof (what if an embedded type I don't control some day adds a custom MarshalJSON() method?)

Are there any alternatives here that I haven't considered?

  • 写回答

1条回答 默认 最新

  • dongquxiao8545 2016-07-20 20:47
    关注

    Don't put MarshalJSON on Person since that's being promoted to the outer type. Instead make a type Name string and have Name implement MarshalJSON. Then change Person to

    type Person struct {
        Name Name `json:"name"`
    }
    

    Example: https://play.golang.org/p/u96T4C6PaY


    Update

    To solve this more generically you're going to have to implement MarshalJSON on the outer type. Methods on the inner type are promoted to the outer type so you're not going to get around that. You could have the outer type call the inner type's MarshalJSON then unmarshal that into a generic structure like map[string]interface{} and add your own fields. This example does that but it has a side effect of changing the order of the final output fields

    https://play.golang.org/p/ut3e21oRdj

    评论

报告相同问题?

悬赏问题

  • ¥15 一道python难题2
  • ¥15 一道python难题
  • ¥15 用matlab 设计一个不动点迭代法求解非线性方程组的代码
  • ¥15 牛顿斯科特系数表表示
  • ¥15 arduino 步进电机
  • ¥20 程序进入HardFault_Handler
  • ¥15 oracle集群安装出bug
  • ¥15 关于#python#的问题:自动化测试
  • ¥20 问题请教!vue项目关于Nginx配置nonce安全策略的问题
  • ¥15 教务系统账号被盗号如何追溯设备