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

时间:2013-08-06 18:56:07

标签: json go

我有这样的结构:

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

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

"data":{}

我知道encoding/json文档指定“空”字段为:

  

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

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

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

4 个答案:

答案 0 :(得分:97)

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

修正:

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

注意*MyStruct - 当我现在创建MyStruct时,我只是参考:

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

现在,“空”MyStruct不再按照需要编组到JSON中。

答案 1 :(得分:7)

正如@chakrit在评论中提到的那样,你无法通过在json.Marshaler上实现MyStruct来实现这一点,并且在每个使用它的结构上实现自定义JSON编组功能可能会很多更多的工作。这取决于你的用例是否值得额外的工作,或者你是否准备好在你的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代码完成,所以我更喜欢内联以避免混乱。

答案 2 :(得分:6)

Data是一个初始化的结构,所以它不被认为是空的,因为encoding/json只查看立即值,而不是结构内的字段。

很遗憾,从nil返回json.Marhsler目前无效:

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

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

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

答案 3 :(得分:0)

此功能具有出色的Golang proposal,该功能已经使用了4年以上,因此,可以安全地假设它不会很快进入标准库。正如@Matt指出的那样,传统方法是将结构转换为 pointers-to-structs 。如果这种方法不可行(或不切实际),那么另一种方法是使用备用json编码器,该编码器支持省略零值结构

我创建了Golang json库( clarketm/json )的镜像,并在<{>}标签为omitempty时添加了对省略零值结构的支持。应用。该库以类似于go-yaml的流行YAML编码器recursively checking the public struct fields的方式检测零度

例如

$ go get -u "github.com/clarketm/json"
import (
    "fmt"
    "github.com/clarketm/json" // drop-in replacement for `encoding/json`
)

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

j, _ := json.Marshal(&Result{
    Status: "204",
    Reason: "No Content",
})

fmt.Println(string(j))
// Note: `data` is omitted from the resultant json.
{
  "status": "204"
  "reason": "No Content"
}