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 for-range 的 value值地址每次都一样的原因解析

    Go for-range 的 value值地址每次都一样的原因解析

    循环语句是一种常用的控制结构,在 Go 语言中,除了 for 关键字以外,还有一个 range 关键字,可以使用 for-range 循环迭代数组、切片、字符串、map 和 channel 这些数据类型,这篇文章主要介绍了Go for-range 的 value值地址每次都一样的原因解析,需要的朋友可以参考下
    2023-05-05
  • Golang实现断点续传功能

    Golang实现断点续传功能

    这篇文章主要为大家详细介绍了Golang实现断点续传、复制文件功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-07-07
  • Go语言类型内嵌和结构体内嵌的具体使用

    Go语言类型内嵌和结构体内嵌的具体使用

    本文主要介绍了Go语言类型内嵌和结构体内嵌的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • Go语言中make和new函数的用法与区别

    Go语言中make和new函数的用法与区别

    这篇文章介绍了Go语言中make和new函数的用法与区别,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07
  • 浅谈goland导入自定义包时出错(一招解决问题)

    浅谈goland导入自定义包时出错(一招解决问题)

    这篇文章主要介绍了浅谈goland导入自定义包时出错(一招解决问题),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • go run main.go 一直提示找不到包的解决方案

    go run main.go 一直提示找不到包的解决方案

    这篇文章主要介绍了go run main.go 一直提示找不到包的解决方案,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Golang error使用场景介绍

    Golang error使用场景介绍

    我们在使用Golang时,不可避免会遇到异常情况的处理,与Java、Python等语言不同的是,Go中并没有try...catch...这样的语句块,这个时候我们如何才能更好的处理异常呢?本文来教你正确方法
    2023-03-03
  • 浅析Go语言中的超时控制

    浅析Go语言中的超时控制

    日常开发中我们大概率会遇到超时控制的场景,而一个良好的超时控制可以有效的避免一些问题,所以本文就来和大家深入探讨一下Go语言中的超时控制吧
    2023-10-10
  • Go 并发实现协程同步的多种解决方法

    Go 并发实现协程同步的多种解决方法

    这篇文章主要介绍了Go 并发——实现协程同步的多种解决方法,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-08-08
  • golang常用库之gorilla/mux-http路由库使用详解

    golang常用库之gorilla/mux-http路由库使用详解

    这篇文章主要介绍了golang常用库之gorilla/mux-http路由库使用,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10

最新评论