首页 > 解决方案 > 有没有办法在不为空时将不同的结构类型编组到相同的 json 字段?

问题描述

我在这样的通用包中定义了一个响应结构。

package main

import (
    "encoding/json"
    "fmt"
)

type Response struct {
    Id string `json:"id,omitempty"`
    //All other int/string fields

    NestedObj *Nested `json:"nested,omitempty"`
}

type Nested struct {
    //Field causing the issue.
    ExtendedString string `json:"extended,omitempty"` //some user will need/expect a string

    //Other user need extended json field as a different object
    Extended *Extended `json:"extended,omitempty"` //changing json:extended to other json:extended1 will work as expected
                                                    //But need to have the same field name containing object or just a string field
}

type Extended struct {
    OtherField string `json:"another,omitempty"`
}

func main() {
    response := &Response{Id: "UNIQUE_ID"}

    response.NestedObj = &Nested{}

    //Part 1
    response.NestedObj.ExtendedString = "value"

    b, err := json.Marshal(response)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(string(b))
    //Part 1 end

    
    response1 := &Response{Id: "UNIQUE_ID"}

    response1.NestedObj = &Nested{}

    //Part 2
    response1.NestedObj.Extended = &Extended{}

    response1.NestedObj.Extended.OtherField = "value"

    b1, err := json.Marshal(response1)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(string(b1))
    //Part 2 end
    
    //Either Part 1 or 2 will be set for user type as per preference/flag/condition. 

}


现在,由于我在 struct 中使用指针来不导出 json 中的任何空字段/对象,所以我想表现得像当我设置ExtendedString为某个字符串值并省略Extended自定义结构时,我的 json 应该看起来像

{"id":"UNIQUE_ID",...other json fields..., "nested":{"extended": "value"}}

但是当我设置Extended struct ignoringExtendedString我应该得到 json 作为

{"id":"UNIQUE_ID",...other json fields..., "nested":{"extended": {"another":"value"}}}

if可以肯定的是,根据用户偏好(或switch条件可能),在编组的 json 中将需要其中任何一个,因为它们必须具有字段名称,extended该名称可以是字符串或包含其他字段的 json 对象。

是否可以在不复制主结构的情况下实现这一点?

标签: jsongostruct

解决方案


json.MarshalMarshalJSON如果存在,将调用您的类型方法。使用它,您可以检查您的条件,然后返回相应的 JSON。

package main

import (
    "fmt"
    "encoding/json"
)

type ExtendedObj struct {
    Value string `json:"another"`
}

type Extended struct {
    str string
    Obj ExtendedObj `json:"extended"`
}

func (e* Extended) MarshalJSON() ([]byte, error) {
    if e.str != "" {
        return json.Marshal(struct {
            Value string `json:"extended"`
        }{e.str})
    }
    
    return json.Marshal(e)
}

func main() {
    v1 := Extended{"Hey!", ExtendedObj{}}
    jsonBytes, err := json.Marshal(&v1)
    
    if err != nil {
        fmt.Println("Error", err)
    }
    
    fmt.Println(string(jsonBytes))
    
    v2 := Extended{"", ExtendedObj{"Hey!"}}
    jsonBytes2, err := json.Marshal(v2)
    
    fmt.Println(string(jsonBytes2))
}

推荐阅读