Golang JSON编组:如何省略空的嵌套结构体

69

go playground

如上代码所示,可以使用json:",omitempty"来省略结构体中的某些字段在 json 中的显示。

例如:

type ColorGroup struct {
    ID     int `json:",omitempty"`
    Name   string
    Colors []string
}
type Total struct {
    A ColorGroup`json:",omitempty"`
    B string`json:",omitempty"`
}
group := Total{
       A: ColorGroup{},

}

在这种情况下,B不会出现在json.Marshal(group)中。

然而,如果

group := Total{
       B:"abc",

}

A 仍然显示在 json.Marshal(group) 中。

{"A":{"Name":"","Colors":null},"B":"abc"}

问题是如何仅获取

{"B":"abc"}

编辑: 经过一些搜索,这里有一个建议使用指针,换句话说,将Total改为

type Total struct {
    A *ColorGroup`json:",omitempty"`
    B string`json:",omitempty"`
}

3
如果对某些人不起作用,则结构标记中 omitempty 前面的逗号很重要。当我省略它时,即使字段非空也始终被省略。 - Lobsterman
2
以防万一,如果有人遇到我的问题...不要在逗号和omitempty之间放置空格。 - joninx
也许这个答案可以帮到你?https://dev59.com/vmMl5IYBdhLWcg3w1p26#18088527 - Marcos
3个回答

65

根据文档

结构体值编码为JSON对象。每个导出的结构体字段都成为该对象的成员,除非

  • 该字段的标记为“-”,或者
  • 该字段为空并且其标记指定了“omitempty”选项。

空值包括false、0、任何nil指针或接口值以及长度为零的任何数组、切片、映射或字符串。

在您声明group时,隐含了group.A将是ColorGroup结构类型的零值。请注意,结构类型的零值未在那个被认为是“空值”的列表中提到。

如您所发现,对于您的情况,解决方法是使用指针。如果您在声明group时不指定A,则这将起作用。如果您将其指定为指向零结构的指针,则它将再次出现。

playground链接

package main

import (
    "encoding/json"
    "fmt"
    "os"
)

func main() {
    type colorGroup struct {
        ID     int `json:",omitempty"`
        Name   string
        Colors []string
    }
    type total struct {
        A *colorGroup `json:",omitempty"`
        B string     `json:",omitempty"`
    }

    groupWithNilA := total{
        B: "abc",
    }
    b, err := json.Marshal(groupWithNilA)
    if err != nil {
        fmt.Println("error:", err)
    }
    os.Stderr.Write(b)

    println()

    groupWithPointerToZeroA := total{
        A: &colorGroup{},
        B: "abc",
    }
    b, err = json.Marshal(groupWithPointerToZeroA)
    if err != nil {
        fmt.Println("error:", err)
    }
    os.Stderr.Write(b)
}

7
我知道这是一篇旧帖子,但我有一个问题。如果使用指针是一种变通方法,那么其中的注意点是什么? - rrw
2
为什么不总是对于结构体类型的字段使用指针? - sdfsdf
我知道这些是旧评论,但是“关键”和“为什么不总是使用指针”是因为我们必须始终检查指针是否为nil,否则在访问nil结构字段时会导致进程崩溃。 if g.A.Name != "" {}; if g.A.ID == 0 {} 将变成 if g.A != nil { if g.A.Name != "" { } // Colors and ID check...} - Delicious Bacon

2
这是一种替代方案,以避免使用指向结构体的指针。 Container 结构体实现了 json.Marshaller 接口,这使我们可以决定应省略哪些结构成员。 https://play.golang.com/p/hMJbQ-QQ5PU
package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    for _, c := range []Container{
        {},
        {
            Element: KeyValue{
                Key:   "foo",
                Value: "bar",
            },
        },
    } {
        b, err := json.Marshal(c)
        if err != nil {
            panic(err)
        }
        fmt.Println(string(b))
    }
}

type Container struct {
    Element KeyValue
}

func (c Container) MarshalJSON() ([]byte, error) {
    // Alias is an alias type of Container to avoid recursion.
    type Alias Container

    // AliasWithInterface wraps Alias and overrides the struct members,
    // which we want to omit if they are the zero value of the type.
    type AliasWithInterface struct {
        Alias
        Element interface{} `json:",omitempty"`
    }

    return json.Marshal(AliasWithInterface{
        Alias:   Alias(c),
        Element: c.Element.jsonValue(),
    })
}

type KeyValue struct {
    Key   string
    Value string
}

// jsonValue returns nil if kv is the zero value of KeyValue. It returns kv otherwise.
func (kv KeyValue) jsonValue() interface{} {
    var zero KeyValue
    if kv == zero {
        return nil
    }
    return kv
}

编辑:添加了文档


-20

简单的方法

type <name> struct {
< varname > < vartype > \`json : -\`
}

示例:

type Boy struct {
name string \`json : -\`
}

这种方式在编组时不会序列化name


7
至少有15个人对此进行了负面评价,但没有向我们解释原因。 - user7401700
7
@JeshanBabooa,1. name 字段未导出!导出的字段首字母必须大写。2. 当字段被导出时,json:"-" 才会生效。3. 最后一句话:如果字段被导出,则 json:"-" 将完全忽略它。 - Mamrezo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接