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

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

golang json marshal: how to omit empty nested struct

问题

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"`
}
英文:

go playground

As shown in the code above, one can use json:",omitempty" to omit certain fields in a struct to appear in json.

For example

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{},
       
}

In this case, B won't show up in json.Marshal(group)

However, if

group := Total{
       B:"abc",
       
}

A still shows up in json.Marshal(group)

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

Question is how do we get only

{"B":"abc"}

EDIT:
After some googling, here is a suggestion use pointer, in other words, turn Total into

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

答案1

得分: 65

文档中可以看到:

结构体值会被编码为JSON对象。除非:

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

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

在你声明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)
}
英文:

From the documentation:

>Struct values encode as JSON objects. Each exported struct field becomes a member of the object unless
>
>- the field's tag is "-", or
>- the field is empty and its tag specifies the "omitempty" option.
>
>The empty values are false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero.

In your declaration of group, it's implicit that group.A will be the zero value of the ColorGroup struct type. And notice that zero-values-of-struct-types is not mentioned in that list of things that are considered "empty values".

As you found, the workaround for your case is to use a pointer. This will work if you don't specify A in your declaration of group. If you specify it to be a pointer to a zero-struct, then it will show up again.

playground link:

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

答案2

得分: 2

这是一种替代方案,以防您不想使用指向结构体的指针。Container 结构体实现了 json.Marshaller 接口,这使我们可以决定哪些结构体成员应该被省略。

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
}

编辑:添加了文档说明

英文:

This is an alternative solution, in case you would like to avoid using pointers to structs. The Container struct implements json.Marshaller, which allows us to decide which members of the strcut should be omitted.

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
}

EDIT: added documentation

答案3

得分: -20

简单的方法

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

// 示例:
type Boy struct {
    name string `json:"-"`
}

这种方式在进行序列化时,name 字段将不会被序列化。

英文:

Easy way

type &lt;name&gt; struct {
&lt; varname &gt; &lt; vartype &gt; \`json : -\`
}

Example :

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

this way on marshaling name will not get serialized.

huangapple
  • 本文由 发表于 2015年10月31日 10:03:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/33447334.html
匿名

发表评论

匿名网友

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

确定