Golang中如何实现JSON序列化时忽略nil值(非自定义结构体)
Golang中如何实现JSON序列化时忽略nil值(非自定义结构体) 我使用 Azure SDK 中的一些类型,想将其转换为 JSON 作为工具的输出,但由于存在许多空值,输出内容相当冗杂,我只想要有意义的数据。
我知道可以创建自己的结构体并使用 omitempty 标签,但这些结构体来自其他包。
有什么想法吗?
你的问题不够明确。
- 你是只想将感兴趣的字段解析到结构体中吗?
- 还是你想将数据解析到现有结构体中,但只打印该结构体中非空字段?
更多关于Golang中如何实现JSON序列化时忽略nil值(非自定义结构体)的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我也是这么想的。
不过,我有个主意,我会使用 https://github.com/fatih/structs 将结构体转换为映射,然后将其序列化为 JSON 文本。
Azure SDK 返回的结构体不受我控制
我正在编写一个命令行工具来协助完成其他使用该 SDK 的任务。
我的命令行工具会将这些 SDK 结构体序列化为 JSON 格式,并将其响应写入标准输出。默认情况下,无论字段值如何,所有字段都会被写入 JSON 输出。
我希望省略 nil/null 值。
所以您的两个选项都不适用。我没有任何需要解析的内容。SDK 已经完成了解析工作。
请参考 json.Marshal 的文档:
“omitempty” 选项指定当字段值为空时应从编码中省略该字段。空值的定义包括:false、0、nil 指针、nil 接口值,以及任何空数组、切片、映射或字符串。
但如果你无法控制该类型,那就无能为力了。
你可以尝试使用类似 jq 的工具来处理 JSON。
在Go语言中,处理来自第三方包(如Azure SDK)的结构体时,确实无法直接修改其字段标签。不过,可以通过几种方式实现JSON序列化时忽略nil值。
方法1:使用自定义MarshalJSON方法
最直接的方式是为这些类型创建包装器并实现自定义的MarshalJSON方法:
package main
import (
"encoding/json"
"reflect"
)
// 创建通用函数处理nil值过滤
func marshalWithoutNil(v interface{}) ([]byte, error) {
return json.Marshal(removeNilFields(v))
}
func removeNilFields(v interface{}) interface{} {
val := reflect.ValueOf(v)
// 如果是nil或者零值,直接返回
if !val.IsValid() || val.IsZero() {
return nil
}
// 如果是指针,解引用
if val.Kind() == reflect.Ptr {
if val.IsNil() {
return nil
}
val = val.Elem()
}
// 处理结构体
if val.Kind() == reflect.Struct {
result := make(map[string]interface{})
typ := val.Type()
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
fieldName := typ.Field(i).Name
// 跳过非导出字段
if !field.CanInterface() {
continue
}
fieldValue := removeNilFields(field.Interface())
if fieldValue != nil {
result[fieldName] = fieldValue
}
}
if len(result) == 0 {
return nil
}
return result
}
// 处理切片
if val.Kind() == reflect.Slice {
if val.Len() == 0 {
return nil
}
result := make([]interface{}, 0, val.Len())
for i := 0; i < val.Len(); i++ {
item := removeNilFields(val.Index(i).Interface())
if item != nil {
result = append(result, item)
}
}
if len(result) == 0 {
return nil
}
return result
}
// 处理map
if val.Kind() == reflect.Map {
if val.Len() == 0 {
return nil
}
result := make(map[string]interface{})
iter := val.MapRange()
for iter.Next() {
key := iter.Key().Interface()
value := removeNilFields(iter.Value().Interface())
if value != nil {
result[toString(key)] = value
}
}
if len(result) == 0 {
return nil
}
return result
}
return v
}
func toString(v interface{}) string {
switch s := v.(type) {
case string:
return s
default:
return reflect.ValueOf(v).String()
}
}
使用示例:
// 假设这是Azure SDK中的类型
type AzureResource struct {
Name *string
ID *string
Location *string
Tags map[string]string
}
func main() {
resource := &AzureResource{
Name: stringPtr("my-resource"),
ID: nil,
Location: nil,
Tags: nil,
}
// 使用自定义序列化
data, err := marshalWithoutNil(resource)
if err != nil {
panic(err)
}
fmt.Println(string(data))
// 输出: {"Name":"my-resource"}
}
func stringPtr(s string) *string {
return &s
}
方法2:使用反射创建过滤后的副本
func createFilteredCopy(src interface{}) interface{} {
srcVal := reflect.ValueOf(src)
if srcVal.Kind() == reflect.Ptr {
if srcVal.IsNil() {
return nil
}
srcVal = srcVal.Elem()
}
if srcVal.Kind() != reflect.Struct {
return src
}
srcType := srcVal.Type()
filtered := make(map[string]interface{})
for i := 0; i < srcVal.NumField(); i++ {
field := srcVal.Field(i)
fieldName := srcType.Field(i).Name
if !field.CanInterface() {
continue
}
// 检查是否为nil或零值
if !field.IsZero() {
fieldVal := field.Interface()
// 如果是指针,检查是否为nil
if field.Kind() == reflect.Ptr {
if !field.IsNil() {
filtered[fieldName] = field.Elem().Interface()
}
} else {
filtered[fieldName] = fieldVal
}
}
}
return filtered
}
// 使用方式
func marshalAzureResource(resource interface{}) ([]byte, error) {
filtered := createFilteredCopy(resource)
return json.Marshal(filtered)
}
方法3:使用第三方库
如果允许使用第三方库,可以考虑使用支持灵活序列化选项的库:
import "github.com/tidwall/gjson"
// 或者使用更灵活的JSON处理库
func marshalWithOptions(v interface{}) ([]byte, error) {
// 先序列化,然后使用gjson等库进行过滤
data, err := json.Marshal(v)
if err != nil {
return nil, err
}
// 这里可以添加更复杂的过滤逻辑
return data, nil
}
推荐使用第一种方法,因为它提供了最灵活的控制,能够递归处理嵌套结构体、切片和映射,确保所有层级的nil值都被正确过滤。

