英文:
Golang / Go - How to marshal struct to null if it has no fields?
问题
我知道这可能看起来是一个常见的问题,但请耐心等待...
我有一个结构体,其中一个字段的类型与另一个结构体相同:
type Developer struct {
Name string `json:"name,omitempty"`
ProjectRef *Ref `json:"project,omitempty"`
}
type Ref struct {
ID string `json:"id,omitempty"`
}
在我的实现中,我无法保证是否会有一个 Developer 的 ProjectRef。如果我创建一个 ID 为空的 Ref,即一个空字符串,那么这个字段会从 Ref 中省略掉,然而,即使在这一点上我的 Ref 没有字段,为什么它不会被省略为空呢?
我猜克服这个问题的一种方法是使用一堆条件语句,但我不想这样做,因为我有很多情况下都需要这个功能。
完整的演示代码:
package main
import (
"encoding/json"
"fmt"
)
type Developer struct {
Name string `json:"name,omitempty"`
ProjectRef *Ref `json:"project,omitempty"`
}
type Ref struct {
ID string `json:"id,omitempty"`
}
func main() {
developer := &Developer{
Name: "Charlie",
ProjectRef: &Ref{ID: ""},
}
jsonBytes, err := json.Marshal(developer)
if err != nil {
panic(err)
}
fmt.Println(string(jsonBytes))
// {"name":"Charlie","project":{}}
}
链接到 playground: https://go.dev/play/p/D2edbrACXY2
提前谢谢你。
英文:
I know this would probably seem a common question, but bare with me...
I have a struct with one of the fields type equal to that of another struct:
type Developer struct {
Name string `json:"name,omitempty"`
ProjectRef *Ref `json:"project,omitempty"`
}
type Ref struct {
ID string `json:"id,omitempty"`
}
In my implementation I cannot guarantee if there is or isn't going to be a ProjectRef for a Developer. If I create a Ref with a null ID, i.e. an empty string, then this field is omitted from the Ref, however, even though my Ref has no fields at this point, why is it not omitted from being empty?
I guess one way of overcoming this would be with a bunch of conditional statements, but I don't want to bring myself to doing that because I have a lot of cases where this functionality is desired.
Full demo code:
package main
import (
"encoding/json"
"fmt"
)
type Developer struct {
Name string `json:"name,omitempty"`
ProjectRef *Ref `json:"project,omitempty"`
}
type Ref struct {
ID string `json:"id,omitempty"`
}
func main() {
developer := &Developer{
Name: "Charlie",
ProjectRef: &Ref{ID: ""},
}
jsonBytes, err := json.Marshal(developer)
if err != nil {
panic(err)
}
fmt.Println(string(jsonBytes))
// {"name":"Charlie","project":{}}
}
Link to playground: https://go.dev/play/p/D2edbrACXY2
Thank you in advance
答案1
得分: 2
结构体即使所有字段都包含零值,也会进行编组。
一种方法是将Developer.ProjectRef
字段保留为nil
,并移除omitempty
属性。这样它将被编组为JSON的null
值:
type Developer struct {
Name string `json:"name,omitempty"`
ProjectRef *Ref `json:"project"`
}
测试代码:
developer := &Developer{
Name: "Charlie",
}
jsonBytes, err := json.Marshal(developer)
if err != nil {
panic(err)
}
fmt.Println(string(jsonBytes))
developer.ProjectRef = &Ref{ID: "abc"}
jsonBytes, err = json.Marshal(developer)
if err != nil {
panic(err)
}
fmt.Println(string(jsonBytes))
输出结果(在Go Playground上尝试):
{"name":"Charlie","project":null}
{"name":"Charlie","project":{"id":"abc"}}
如果你希望Developer.ProjectRef
始终是非nil
指针,那么可以为Ref
编写一个自定义的JSON编组器,如果ID为空,则将其编组为JSON的null
值:
func (r *Ref) MarshalJSON() ([]byte, error) {
if r.ID == "" {
return []byte("null"), nil
}
type ref2 Ref
return json.Marshal((*ref2)(r))
}
测试代码:
developer := &Developer{
Name: "Charlie",
ProjectRef: &Ref{ID: ""},
}
jsonBytes, err := json.Marshal(developer)
if err != nil {
panic(err)
}
fmt.Println(string(jsonBytes))
developer.ProjectRef.ID = "abc"
jsonBytes, err = json.Marshal(developer)
if err != nil {
panic(err)
}
fmt.Println(string(jsonBytes))
输出结果(在Go Playground上尝试):
{"name":"Charlie","project":null}
{"name":"Charlie","project":{"id":"abc"}}
英文:
Structs are marshaled even if all their fields hold the zero values.
One way is to leave the Developer.ProjectRef
field nil
, and remove the omitempty
attribute. That way it will be marshaled into the JSON null
value:
type Developer struct {
Name string `json:"name,omitempty"`
ProjectRef *Ref `json:"project"`
}
Testing it:
developer := &Developer{
Name: "Charlie",
}
jsonBytes, err := json.Marshal(developer)
if err != nil {
panic(err)
}
fmt.Println(string(jsonBytes))
developer.ProjectRef = &Ref{ID: "abc"}
jsonBytes, err = json.Marshal(developer)
if err != nil {
panic(err)
}
fmt.Println(string(jsonBytes))
Output (try it on the Go Playground):
{"name":"Charlie","project":null}
{"name":"Charlie","project":{"id":"abc"}}
If you always want Developer.ProjectRef
to be a non-nil
pointer, then write a custom JSON marshaler for Ref
, which could marshal the JSON null
value if the ID is empty:
func (r *Ref) MarshalJSON() ([]byte, error) {
if r.ID == "" {
return []byte("null"), nil
}
type ref2 Ref
return json.Marshal((*ref2)(r))
}
Testing it:
developer := &Developer{
Name: "Charlie",
ProjectRef: &Ref{ID: ""},
}
jsonBytes, err := json.Marshal(developer)
if err != nil {
panic(err)
}
fmt.Println(string(jsonBytes))
developer.ProjectRef.ID = "abc"
jsonBytes, err = json.Marshal(developer)
if err != nil {
panic(err)
}
fmt.Println(string(jsonBytes))
Output (try it on the Go Playground):
{"name":"Charlie","project":null}
{"name":"Charlie","project":{"id":"abc"}}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论