JSON 同时实现 MarshalJSON() 的复合结构的编组

JSON Marshaling of composite structs which both implement MarshalJSON()

我最近遇到了以下问题,还没有找到任何解决方案。我在 Go 中有两种结构类型,我们称它们为 Parent 和 Child。 Child 有一个 *Parent 类型的匿名字段。但是,Parent 有一个名为 "ID" 的字段,它具有第三个结构的类型,我们将其称为 "IDType"(在我的实际问题中,这是一个 dialect/sql.NullInt64)。 IDType 有一个 int 字段和一个 bool 字段。

问题如下:Parent 和 Child 都实施 MarshalJSON() 因为对于 Parent 我只希望 JSON 中的 int 字段和对于 Child 一样。但是,似乎两个 MarshalJSONs 都推断出只有 Parent 的值在最终 JSON.

中编码的结果

也许一个简单的例子更容易理解:

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)
}

输出为:

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

我希望是这样的:

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

您应该有一个指向父级的命名指针或父级的嵌入值。

选项 1 会给你 JSON 你所期望的。

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

选项 2 将父节点作为子节点。

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

另一个 hacky 选项是单独编组父子,然后通过剪切 last/first 个字符手动加入,加入 , 并包裹在 {}.

由于自定义 ID 封送处理,您似乎只定义了自定义封送处理逻辑。仅为未嵌入的 IDType 类型定义自定义封送处理,因此封送其他类型不会造成任何问题:

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

并且不需要其他自定义封送处理。这样,输出将是:

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

Go Playground 上试用。