Golang / Go – 如何将结构体转换为 null,如果它没有字段?

huangapple go评论87阅读模式
英文:

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

huangapple
  • 本文由 发表于 2022年2月24日 21:28:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/71252747.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定