go omitempty 测试

2023/07/08

使用 go 的返回 json 数据的时候,go 默认会空值设置为默认值,但是在有些业务中不希望返回默认值,所以需要使用 omitempty 修饰从而忽略空值。不过 omitempty 的实际不能并不像你所想象那样,下面这些简单的程序片段用于测试 omitempty 的实际表现。

1. 基础数据结构

1.1 未采用 omitempty

Struct

type Student struct {
    Id   int    `json:"id"`
    Name string `json:"name"`
}

Input

Student{
    Id:   1,
}

Result

{
  "id": 1,
  "name": ""
}

1.2 采用 omitempty

Struct

type Student struct {
    Id   int    `json:"id,omitempty"`
    Name string `json:"name,omitempty"`
}

Input

Student{
    Id:   1
}

Result

{
  "id": 1
}

1.3 当传入参数与默认值相同

Struct

type Student struct {
    Id   int    `json:"id,omitempty"`
    Name string `json:"name,omitempty"`
}

Input

Student{
    Id:   0,
    Name: "tom",
}

Result

{
  "name": "tom"
}

第一个和第二个例子都非常符合预期,但是在第三种,明明给 id 赋值为 0,但是却没有输出结果。所以在使用 omitempty 的时候需要谨慎判断这个值在业务上是否可能为默认值,如果是要么不加上 omitempty 要么将这个值的类型设置为指针类型,否则在输出为 json 的时候就会丢失值。

2. 基础数据结构的指针类型

1.4 保持空值

Struct

type Student struct {
    Id   *int   `json:"id,omitempty"`
    Name string `json:"name,omitempty"`
}

Input

Student{
    Name:  "tom",
}

Resout

{
  "name": "tom"
}

2.2. 设置值为默认值

Struct

type Student struct {
    Id   *int   `json:"id,omitempty"`
    Name string `json:"name,omitempty"`
}

Input

t := Student{}
t.Id = new(int)
*t.Id = 0
t.Name = "tome"

Result

{
  "id": 0,
  "name": "tome"
}

可以看到这里将 id 设置为指针类型之后即使传入的是 int 对应的默认值最后的结果还是有的,因为对于指针类型而言默认值为 nil这一点不仅可以用在处理输出数据上面还可以用在处理输入数据上面,比如上面那个数据结构,id = 0 是合法的,但是如果我们需要判断输入的参数中是否含有 id 这个字段,就可以将 id 设置为指针类型,如果表现为空指针则说明是没有输入这个字段。

3. 复合数据结构

3.1 复合类型的切片

Struct

type Class struct {
    Teachers []Teacher `json:"teachers,omitempty"`
    Students []Student `json:"students,omitempty"`
}

type Teacher struct {
    Id   int
    Name string
}

type Student struct {
    Id   int    `json:"id"`
    Name string `json:"name"`
}

Input

class := Class{}
teacher := Teacher{
    Id:   0,
    Name: "joy",
}
class.Teachers = append(class.Teachers, teacher)

Result

{
  "teachers": [
    {
      "Id": 0,
      "Name": "joy"
    }
  ]
}

可以看到对于复合类型的切片 omitempty 是可以生效的

3.2 复合数据类型

Struct

type Class struct {
    MatheTeacher Teacher `json:"teachers,omitempty"`
    Student      Student `json:"students,omitempty"`
}

type Teacher struct {
    Id   int
    Name string
}

type Student struct {
    Id   int    `json:"id"`
    Name string `json:"name"`
}

Input

class := Class{}
teacher := Teacher{
    Id:   0,
    Name: "joy",
}
class.MatheTeacher = teacher

Result

{
  "teachers": {
    "Id": 0,
    "Name": "joy"
  },
  "students": {
    "id": 0,
    "name": ""
  }
}

可以看到在这里普通的复合数据结构使用 omiempty 修饰并没有生效

3.3 复合数据结构的指针

Struct

type Class struct {
    MatheTeacher *Teacher `json:"teachers,omitempty"`
    Student      *Student `json:"students,omitempty"`
}

Input

class := Class{}
teacher := Teacher{
    Id:   0,
    Name: "joy",
}
class.MatheTeacher = new(Teacher)
*class.MatheTeacher = teacher

Result

{
  "teachers": {
    "Id": 0,
    "Name": "joy"
  }
}

可以看到这里使用 omitempty 可以生效,需要注意的是这里的指针在赋值的时候必须先分配内存地址,否则就会出现空指针错误,并且不会在编译的时候报错,在生产环境中出现这种错误非常麻烦。