go中利用reflect实现json序列化的示例代码

 更新时间:2024年03月26日 08:23:02   作者:uccs  
和Java语言一样,Go也实现运行时反射,这为我们提供一种可以在运行时操作任意类型对象的能力,本文给大家介绍了在go中如何利用reflect实现json序列化,需要的朋友可以参考下

利用反射实现json序列化

type Person struct {
  Name       string `json:"name"`
  Age        int    `json:"age"`
  IsMarraied bool   `json:"is_marraied"`
}
k := map[int]Person{
  1: {Name: "uccs", Age: 18, IsMarraied: false},
  2: {Name: "uccs", Age: 18, IsMarraied: true},
  3: {Name: "uccs", Age: 18, IsMarraied: true},
}

s := &[...]interface{}{
  1,
  &Person{Name: "uccs", Age: 18, IsMarraied: false},
  Person{Name: "uccs", Age: 18, IsMarraied: true},
  true,
  Person{Name: "uccs", Age: 18, IsMarraied: true},
}

reflect.ValueOf() 函数的作用是返回一个包含给定值的 reflect.Value 类型的值

拿到值 rv之后 ,使用 rv.Type().Kind() 就能拿到用户传入值的底层类型

rv.Type() 拿到的值是 reflect.Type 类型,没法用来判断,所以需要使用 rv.Type().Kind() 拿到 reflect.Kind

判断 int 类型

int 类型有 intint8int16int32int64,返回 fmt.Sprintf("%v", rv.Int())

func JsonMarshal(v interface{}) (string, error) {
  rv := reflect.ValueOf(v)
  rt := rv.Type()
  switch rt.Kind() {
  case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    return fmt.Sprintf("%v", rv.Int()), nil
  default:
    return "", fmt.Errorf("unsupported type: %s", rt)
  }
}

判断 float 类型

float 类型有 float32 和 float64,返回 fmt.Sprintf("%v", rv.Float())

func JsonMarshal(v interface{}) (string, error) {
  rv := reflect.ValueOf(v)
  rt := rv.Type()
  switch rt.Kind() {
  case reflect.Float32, reflect.Float64:
    return fmt.Sprintf("%v", rv.Float()), nil
  default:
    return "", fmt.Errorf("unsupported type: %s", rt)
  }
}

判断 string 类型

string 类型,返回 fmt.Sprintf("%q", rv.String())

func JsonMarshal(v interface{}) (string, error) {
  rv := reflect.ValueOf(v)
  rt := rv.Type()
  switch rt.Kind() {
  case reflect.String:
    return fmt.Sprintf("%q", rv.String()), nil
  default:
    return "", fmt.Errorf("unsupported type: %s", rt)
  }
}

判断 bool 类型

bool 类型,返回 fmt.Sprintf("%v", rv.Bool())

func JsonMarshal(v interface{}) (string, error) {
  rv := reflect.ValueOf(v)
  rt := rv.Type()
  switch rt.Kind() {
  case reflect.Bool:
    return fmt.Sprintf("%v", rv.Bool()), nil
  default:
    return "", fmt.Errorf("unsupported type: %s", rt)
  }
}

判断 slice 类型

slice 类型可以简单理解为数组,返回的类型是数组的 json 字符串

func JsonMarshal(v interface{}) (string, error) {
  rv := reflect.ValueOf(v)
  rt := rv.Type()
  switch rt.Kind() {
  case reflect.Slice:
    return marshalSlice(rv)
  default:
    return "", fmt.Errorf("unsupported type: %s", rt)
  }
}

处理 slice 的过程封装为 marshalSlice 函数

需要遍历 slice 拿到每一项的内容,可以通过 `rv.Index(i).Interface()

然后调用 jsonMarshal 函数,传入 slice 中的每一项内容,递归处理

最后拼接出数组格式的 json 字符串,使用 strings.Join(items, ",") 拼接

func marshalSlice(rv reflect.Value) (string, error) {
  var items []string
  for i := 0; i < rv.Len(); i++ {
    value, err := JsonMarshal(rv.Index(i).Interface())
    if err != nil {
      return "", err
    }
    items = append(items, value)
  }
  return "[" + strings.Join(items, ",") + "]", nil
}

判断 array 类型

处理 array 类型和处理 slice 是一样的,只需要将 array 转换为 slice,在反射中可以使用 rv.Slice(0, rv.Len())

func JsonMarshal(v interface{}) (string, error) {
  rv := reflect.ValueOf(v)
  rt := rv.Type()
  switch rt.Kind() {
  case reflect.Array:
    return marshalSlice(rv.Slice(0, rv.Len()))
  default:
    return "", fmt.Errorf("unsupported type: %s", rt)
  }
}

判断 struct 类型

struct 类型类似于对象,返回的类型是对象的 json 字符串

func JsonMarshal(v interface{}) (string, error) {
  rv := reflect.ValueOf(v)
  rt := rv.Type()
  switch rt.Kind() {
  case reflect.Struct:
    return marshalStruct(rv)
  default:
    return "", fmt.Errorf("unsupported type: %s", rt)
  }
}

处理 struct 的过程封装为 marshalStruct 函数

我们先定义一个结构体

type Person struct {
  Name       string `json:"name"`
  Age        int    `json:"age"`
  isMarraied bool   `json:"is_marraied"`
}

这个结构体中,有两个地方需要注意:

  • 小写字母开头的属性,不需要序列化
  • 按照 json_tag 的值来序列化

通过 rv.NumField 获取到结构体中的所有的属性,然后使用 for 循环遍历

通过 rv.Field(i) 获取到属性的值,通过 rv.Type().Field(i).Tag.Get("json") 获取到 json 标签的值

如果属性名是小写字母开头的,不需要序列化,直接跳过,通过 isFieldExported 函数完成

func isFieldExported(name string) bool {
  r, _ := utf8.DecodeRuneInString(name)
  return unicode.IsUpper(r)
}

然后调用 jsonMarshal 函数,传入结构体中的每一项内容,递归处理

最后拼接出数组格式的 json 字符串,使用 strings.Join(items, ",") 拼接

func marshalStruct(rv reflect.Value) (string, error) {
  var items []string
  for i := 0; i < rv.NumField(); i++ {
    fieldValue := rv.Field(i)
    jsonTag := rv.Type().Field(i).Tag.Get("json")
    key := rv.Type().Field(i).Name
    if !isFieldExported(key) {
      continue
    }
    if jsonTag != "" {
      key = jsonTag
    }
    value, err := JsonMarshal(fieldValue.Interface())
    if err != nil {
      return "", err
    }

    items = append(items, fmt.Sprintf("%q:%v", key, value))
  }
  return "{" + strings.Join(items, ",") + "}", nil
}

处理 pointer 类型

处理 pointer 类型,需要先判断 pointer 指向的类型

  • 如果是 array 类型,需要将 pointer 转换为 slice,然后调用 marshalSlice 函数
  • 如果是 struct 类型,直接调用 marshalStruct
func JsonMarshal(v interface{}) (string, error) {
  rv := reflect.ValueOf(v)
  rt := rv.Type()
  switch rt.Kind() {
  case reflect.Pointer:
    if rv.Elem().Kind() == reflect.Array {
      return marshalSlice(rv.Elem().Slice(0, rv.Len()))
    }
    if rv.Elem().Kind() == reflect.Struct {
      return JsonMarshal(rv.Elem().Interface())
    }
    return JsonMarshal(rv.Elem().Interface())
  default:
    return "", fmt.Errorf("unsupported type: %s", rt)
  }
}

对应的源码:to_json

到此这篇关于go中利用reflect实现json序列化的示例代码的文章就介绍到这了,更多相关go reflect json序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解Go sync 同步原语

    详解Go sync 同步原语

    Go 中不仅有 channel 这种 CSP 同步机制,还有 sync.Mutex、sync.WaitGroup 等比较原始的同步原语,使用它们,可以更灵活的控制数据同步和多协程并发,这篇文章主要介绍了Go sync 同步原语,需要的朋友可以参考下
    2023-12-12
  • 使用go求幂的几种方法小结

    使用go求幂的几种方法小结

    这篇文章主要介绍了使用go求幂的几种方法小结,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • goland -sync/atomic原子操作小结

    goland -sync/atomic原子操作小结

    这篇文章主要介绍了goland -sync/atomic原子操作,原子操作能够保证执行期间是连续且不会被中断(变量不会被其他修改,mutex可能存在被其他修改的情况),本文给大家介绍的非常详细,需要的朋友参考下
    2022-08-08
  • Golang内存对齐的规则及实现

    Golang内存对齐的规则及实现

    本文介绍了Golang内存对齐的规则及实现,通过合理的内存对齐,可以提高程序的执行效率和性能,通过对本文的阅读,读者可以更好地理解Golang内存对齐的原理和技巧,并应用于实际编程中
    2023-08-08
  • 在Golang中实现RSA算法的加解密操作详解

    在Golang中实现RSA算法的加解密操作详解

    RSA 是一种非对称加密算法,广泛使用于数据的安全传输,crypto/rsa 是 Golang 中实现了 RSA 算法的一个标准库,提供了生成公私钥对、加解密数据、签名和验签等功能,本文给大家介绍了在Golang中实现RSA算法的加解密操作,需要的朋友可以参考下
    2023-12-12
  • 在VS Code中配置Go开发环境的完整过程

    在VS Code中配置Go开发环境的完整过程

    如果您希望在本地计算机上开始使用Go语言进行开发,但尚未配置好VSCode中的Go支持,则可能是由于缺少必要工具链、扩展或环境变量设置,可以看看这篇文章,这篇文章主要介绍了在VS Code中配置Go开发环境的完整过程,需要的朋友可以参考下
    2026-04-04
  • Go语言append切片添加元素的实现

    Go语言append切片添加元素的实现

    本文主要介绍了Go语言append切片添加元素的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • 基于Go语言实现一个简易远程传屏工具

    基于Go语言实现一个简易远程传屏工具

    今天我要给大家介绍一个用Go语言写的远程传屏工具:这可不是什么高大上的商业软件,而是一个非常实用的小工具,让你的屏幕内容能够轻松飞到另一台电脑上,需要的朋友可以参考下
    2025-11-11
  • Go语言学习技巧之如何合理使用Pool

    Go语言学习技巧之如何合理使用Pool

    这篇文章主要给大家介绍了关于Go语言学习技巧之如何合理使用Pool的相关资料,Pool用于存储那些被分配了但是没有被使用,而未来可能会使用的值,以减小垃圾回收的压力。文中通过示例代码介绍的非常详细,需要的朋友可以参考下。
    2017-12-12
  • 浅析Go语言如何在终端里实现倒计时

    浅析Go语言如何在终端里实现倒计时

    这篇文章主要为大家详细介绍了Go语言中是如何在终端里实现倒计时的,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-03-03

最新评论