首页 文章

如何不使用Go将空结构编组为JSON?

提问于
浏览
50

我有这样的结构:

type Result struct {
    Data       MyStruct  `json:"data,omitempty"`
    Status     string    `json:"status,omitempty"`
    Reason     string    `json:"reason,omitempty"`
}

但即使MyStruct的实例完全为空(意味着所有值都是默认值),它也被序列化为:

"data":{}

我知道encoding/json docs指定"empty"字段是:

false,0,任何nil指针或接口值,以及长度为零的任何数组,切片,映射或字符串

但没有考虑具有所有空/默认值的结构 . 它的所有字段也都标有 omitempty ,但这没有任何效果 .

如何让JSON包不能封送我的空结构字段?

3 回答

  • 83

    哦!轻松修复:“任何无指针 . ” - 使结构成为指针 .

    固定:

    type Result struct {
        Data       *MyStruct `json:"data,omitempty"`
        Status     string    `json:"status,omitempty"`
        Reason     string    `json:"reason,omitempty"`
    }
    

    注意 *MyStruct - 当我现在创建一个 MyStruct 时,我只是通过引用这样做:

    myStruct := &MyStruct{ /* values */ }
    

    现在"empty" MyStruct 不再按需要编组到JSON中 .

  • 7

    正如@chakrit在评论中提到的那样,你无法通过在 MyStruct 上实现 json.Marshaler 来实现这一点,并且在使用它的每个结构上实现自定义JSON编组功能可能需要做更多的工作 . 这实际上取决于你的用例是否's worth the extra work or whether you'准备好在你的JSON中使用空结构,但这是我用于 Result 的模式:

    type Result struct {
        Data       MyStruct
        Status     string   
        Reason     string    
    }
    
    func (r Result) MarshalJSON() ([]byte, error) {
        return json.Marshal(struct {
            Data     *MyStruct   `json:"data,omitempty"`
            Status   string      `json:"status,omitempty"`
            Reason   string      `json:"reason,omitempty"`
        }{
            Data:   &r.Data,
            Status: r.Status,
            Reason: r.Reason,
        })        
    }
    
    func (r *Result) UnmarshalJSON(b []byte) error {
        decoded := new(struct {
            Data     *MyStruct   `json:"data,omitempty"`
            Status   string      `json:"status,omitempty"`
            Reason   string      `json:"reason,omitempty"`
        })
        err := json.Unmarshal(b, decoded)
        if err == nil {
            r.Data = decoded.Data
            r.Status = decoded.Status
            r.Reason = decoded.Reason
        }
        return err
    }
    

    如果你有很多字段的巨大结构,这可能会变得乏味,特别是稍后更改结构的实现,但是不能重写整个 json 包以满足你的需要(不是一个好主意),这几乎是我能想到的唯一方法在保持非指针 MyStruct 的同时完成此操作 .

    此外,您不必使用内联结构,您可以创建命名结构 . 我使用LiteIDE代码完成,所以我更喜欢内联以避免混乱 .

  • 5

    Data 是初始化的结构,因此它不被视为空,因为 encoding/json 仅查看立即值,而不是结构内的字段 .

    不幸的是,从 json.Marhsler 返回 nil 目前不起作用:

    func (_ MyStruct) MarshalJSON() ([]byte, error) {
        if empty {
            return nil, nil // unexpected end of JSON input
        }
        // ...
    }
    

    你也可以给一个封送者,但这不值得 .

    正如Matt建议的那样,唯一的选择是使 Data 指针并将值设置为 nil .

相关问题