如何不用 Go 将空结构封送到 JSON 中?

我有一个这样的结构:

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

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

"data":{}

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

假,0,任何空指针或接口值,以及任何数组, 长度为零的切片、贴图或字符串

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

如何让 JSON 包对 没有封送我的字段(空结构) ?

100176 次浏览

As the docs say, "any nil pointer." -- make the struct a pointer. Pointers have obvious "empty" values: nil.

修正-使用 struct 指针字段定义类型:

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

然后是这样的值:

result := Result{}

将执行如下命令:

{}

说明: 注意我们的类型定义中的 *MyStruct。JSON 序列化并不关心它是否是一个指针——那是一个运行时细节。因此,将结构字段转换为指针只对编译和运行时有影响)。

请注意,如果您将字段类型从 MyStruct更改为 *MyStruct,您将需要指向 struct 值的指针来填充它,如下所示:

Data: &MyStruct{ /* values */ }

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

不幸的是,从 json.Marshaler返回 nil目前无法工作:

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

你也可以给 Result一个封送处理器,但是它不值得你付出努力。

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

正如@chakrit 在评论中提到的,通过在 MyStruct上实现 json.Marshaler并在每个使用它的结构体上实现一个自定义 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 完成代码,所以我更喜欢内联以避免混乱。

有一个卓越的戈兰 建议书这个功能已经活跃了4年多,所以在这一点上,它是安全的假设,它不会使其成为标准库很快。正如@Matt 指出的,传统的方法是将 结构转换成 指向结构的指针。如果这种方法不可行(或不切实际) ,那么另一种方法是使用支持 省略零值结构的备用 json 编码器。

我创建了一个 Golang json 库(克拉克特姆 · 杰森(Clarketm/json))的镜像,并在应用 omitempty标记时增加了对 省略零值结构的支持。这个库以类似于 递归地检查 < em > public struct 字段的流行 YAML 编码器 加油的方式检测 零度

e.g.

$ 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"
}