Golang / Go - 如果结构没有字段,如何将其编组为 null?

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

在我的实施中,我无法保证是否存在或不会成为开发人员的 ProjectRef。如果我创建一个ID为null的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":{}}
}

Link 去游乐场:https://go.dev/play/p/D2edbrACXY2

提前致谢

即使结构的所有字段都包含 zero values.

,结构也会被编组

一种方法是保留 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 marshaler,它可以编组 JSON null ID为空时的值:

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