douxuanpa8298
2018-06-13 07:59 阅读 248

都实现MarshalJSON()的复合结构的JSON封送处理

I faced recently the following problem and have not found any solution. I have two struct types in Go, let us call them Parent and Child. Child has an anonymous field of the type *Parent. However, Parent has a field called "ID" which has the type of a third struct which type we will call "IDType" (in my real problem this is a dialect/sql.NullInt64). IDType has an int field and a bool field.

The problem is the following: Both, Parent and Child, implement MarshalJSON() because for Parent I only want the int field inside the JSON and for Child the same. However, it seems that both MarshalJSONs infer with the result that only the values of Parent are encoded in the final JSON.

Maybe a minimal example makes it easier to understand:

package main

import (
    "encoding/json"
    "fmt"
    "os"
)

type IDType struct {
    Value int
    Valid bool
}

type Parent struct {
    ID         IDType `json:"id"`
    SomeString string `json:"some_string"`
}

type Child struct {
    *Parent
    Status int `json:"status"`
}

func (parent *Parent) MarshalJSON() ([]byte, error) {
    type Alias Parent
    fmt.Println("Parent")
    return json.Marshal(struct {
        *Alias
        ID int `json:"id"`
    }{
        Alias: (*Alias)(parent),
        ID:    parent.ID.Value,
    })
}

func (child *Child) MarshalJSON() ([]byte, error) {
    type Alias Child
    fmt.Println("Child")
    return json.Marshal(struct {
        *Alias
        Status int `json:"status"`
    }{
        Alias:  (*Alias)(child),
        Status: child.Status,
    })
}

func main() {
    ID := IDType{Value: 1, Valid: true}
    parent := Parent{ID: ID, SomeString: "Hello"}
    child := Child{Parent: &Parent{ID: ID, SomeString: "Hello"}, Status: 1}
    json.NewEncoder(os.Stdout).Encode(&parent)
    json.NewEncoder(os.Stdout).Encode(&child)
}

The output is:

Parent
{"some_string":"Hello","id":1}
Child
Parent
{"some_string":"Hello","id":1}

I expect something like:

Parent
{"some_string":"Hello","id":1}
Child
Parent
{"some_string":"Hello","id":1, "status": 1}
  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

2条回答 默认 最新

  • 已采纳
    douyijin7741 douyijin7741 2018-06-13 08:47

    It looks you only define a custom marshaling logic because of the custom ID marshaling. Define your custom marshaling for the IDType type only which is not embedded, so it won't cause any trouble marshaling the other types:

    func (id *IDType) MarshalJSON() ([]byte, error) {
        return json.Marshal(id.Value)
    }
    

    And no other custom marshaling are required. With this, the output will be:

    {"id":1,"some_string":"Hello"}
    {"id":1,"some_string":"Hello","status":1}
    

    Try it on the Go Playground.

    点赞 评论 复制链接分享
  • douting0585 douting0585 2018-06-13 08:44

    You should either have a named pointer to parent or embeded value of Parent.

    Option 1 will give you JSON you expect.

    type Child struct {
        Parent
        Status int `json:"status"`
    }
    >> {"some_string":"Hello","id":1, "status": 1}
    

    Option 2 will put parent as sub-node.

    type Child struct {
        Parent *Parent
        Status int `json:"status"`
    }
    >> {Parent: {"some_string":"Hello","id":1}, "status": 1}
    

    Another hacky option would be to marshal parent & child separately and then join manually by cutting last/first characters, join with , and wrap in {}.

    点赞 评论 复制链接分享

相关推荐