如何不使用 Go 将空结构编组到 JSON 中?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18088294/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to not marshal an empty struct into JSON with Go?
提问by Matt
I have a struct like this:
我有一个这样的结构:
type Result struct {
Data MyStruct `json:"data,omitempty"`
Status string `json:"status,omitempty"`
Reason string `json:"reason,omitempty"`
}
But even if the instance of MyStruct is entirely empty (meaning, all values are default), it's being serialized as:
但即使 MyStruct 的实例完全为空(意味着所有值都是默认值),它也会被序列化为:
"data":{}
I know that the encoding/jsondocs specify that "empty" fields are:
我知道编码/json文档指定“空”字段是:
false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero
false、0、任何 nil 指针或接口值,以及任何长度为零的数组、切片、映射或字符串
but with no consideration for a struct with all empty/default values. All of its fields are also tagged with omitempty, but this has no effect.
但没有考虑具有所有空/默认值的结构。它的所有字段也都标有omitempty,但这没有任何影响。
How can I get the JSON package to notmarshal my field that is an empty struct?
如何让 JSON 包不封送我的空结构字段?
回答by Matt
As the docs say, "any nil pointer." -- make the struct a pointer. Pointers have obvious "empty" values: nil.
正如文档所说,“任何 nil 指针”。-- 使结构成为指针。指针具有明显的“空”值:nil.
Fix - define the type with a struct pointerfield:
修复 - 使用结构指针字段定义类型:
type Result struct {
Data *MyStruct `json:"data,omitempty"`
Status string `json:"status,omitempty"`
Reason string `json:"reason,omitempty"`
}
Then a value like this:
然后是这样的值:
result := Result{}
Will marshal as:
将编组为:
{}
Explanation: Notice the *MyStructin our type definition. JSON serialization doesn't care whether it is a pointer or not -- that's a runtime detail. So making struct fields into pointers only has implications for compiling and runtime).
说明:注意*MyStruct我们的类型定义中的 。JSON 序列化并不关心它是否是一个指针——这是一个运行时细节。因此,将 struct 字段转换为指针仅对编译和运行时有影响)。
Just note that if you do change the field type from MyStructto *MyStruct, you will need pointers to struct values to populate it, like so:
请注意,如果您确实将字段类型从MyStructto更改为*MyStruct,您将需要指向结构值的指针来填充它,如下所示:
Data: &MyStruct{ /* values */ }
回答by Leylandski
As @chakrit mentioned in a comment, you can't get this to work by implementing json.Marshaleron MyStruct, and implementing a custom JSON marshalling function on every struct that uses it can be a lot more work. It really depends on your use case as to whether it's worth the extra work or whether you're prepared to live with empty structs in your JSON, but here's the pattern I use applied to Result:
正如@chakrit 在评论中提到的那样,您无法通过实现json.Marshaleron来使其工作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
}
If you have huge structs with many fields this can become tedious, especially changing a struct's implementation later, but short of rewriting the whole jsonpackage to suit your needs (not a good idea), this is pretty much the only way I can think of getting this done while still keeping a non-pointer MyStructin there.
如果您有包含许多字段的巨大结构,这可能会变得乏味,尤其是稍后更改结构的实现,但没有重写整个json包以满足您的需要(不是一个好主意),这几乎是我能想到的唯一方法这样做的同时仍然MyStruct在那里保留一个非指针。
Also, you don't have to use inline structs, you can create named ones. I use LiteIDE with code completion though, so I prefer inline to avoid clutter.
此外,您不必使用内联结构,您可以创建命名结构。我使用 LiteIDE 和代码完成,所以我更喜欢内联以避免混乱。
回答by Luke
Datais an initialized struct, so it isn't considered empty because encoding/jsononly looks at the immediate value, not the fields inside the struct.
Data是一个初始化的结构,所以它不被认为是空的,因为encoding/json只查看立即值,而不是结构内的字段。
Unfortunately returning nilfrom json.Marhslercurrently doesn't work:
不幸的是,nil从json.Marhsler当前返回不起作用:
func (_ MyStruct) MarshalJSON() ([]byte, error) {
if empty {
return nil, nil // unexpected end of JSON input
}
// ...
}
You could give Resulta marshaler as well, but it's not worth the effort.
您也可以提供Result封送员,但这不值得付出努力。
The only option, as Matt suggests, is to make Dataa pointer and set the value to nil.
正如马特建议的那样,唯一的选择是创建Data一个指针并将值设置为nil。
回答by Travis Clarke
There is an outstanding Golang proposalfor this feature which has been active for over 4 years, so at this point, it is safe to assume that it will not make it into the standard library anytime soon. As @Matt pointed out, the traditionalapproach is to convert the structsto pointers-to-structs. If this approach is infeasible (or impractical), then an alternative is to use an alternate json encoder which does support omitting zero value structs.
有一个关于这个特性的优秀 Golang提案已经活跃了 4 年多,所以在这一点上,可以安全地假设它不会很快进入标准库。正如@Matt 指出的那样,传统的方法是将structs转换为pointers-to-structs。如果这种方法不可行(或不切实际),那么另一种方法是使用替代的 json 编码器,它确实支持省略零值 structs。
I created a mirror of the Golang json library (clarketm/json) with added support for omitting zero value structswhen the omitemptytag is applied. This library detects zeronessin a similar manner to the popular YAML encoder go-yamlby recursively checking the publicstruct fields.
我创建了 Golang json 库 ( clarketm/json)的镜像,并添加了对在应用标签时省略零值结构的支持omitempty。该库通过递归检查公共结构字段以类似于流行的 YAML 编码器go-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"
}

