083-反射(序列化 json)
Posted --Allen--
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了083-反射(序列化 json)相关的知识,希望对你有一定的参考价值。
想必你已经掌握了反射的相关操作,也能够遍历结构体的字段和方法,现在我们就要进入实战了。
1. 问题
正如标题所指,我们的目标是能将任意类型的结构体序列化成 json. 对应的 golang 的encoding/jsong 包就是 Marshal 函数, C++ 的 jsoncpp 库来说就是 write 或 fastwrite 方法,对应到 javascript 就是 JSON.stringify 函数了。
你可以在你的浏览器中打开 console 控制台,输入下面的语句:
var p = name: 'allen', age: 11;
JSON.stringify(p);
你会看到类似一个结构体或者 map 对象被转成了字符串。是的,我们要在 golang 中完成类似 stringify 这样的函数。如果你还记得 golang 的 encoding/json 包的话,一定记得当时我们还用过 Marshal 这个函数,它可以把任意的类型序列化成一个 json 串。
2. 程序
2.1 主程序
// main.go
package main
import "fmt"
type Person struct
Name string
age int32
friends []*Person
func main()
zoro := Person
Name: "zoro",
age: 10,
luffy := Person
Name: "luffy",
age: 18,
friends: []*Person
&zoro,
,
allen := Person
Name: "allen",
age: 19,
friends: []*Person
&luffy,
&zoro,
,
// 我们的目标是将这个 map 序列化成 json 串
persons := map[string]Person
"allen": allen,
"luffy": luffy,
"zoro": zoro,
// Parse 就是我们要编写的函数
buf, err := Parse(persons)
if err != nil
fmt.Printf("%v\\n", err)
return
fmt.Printf("%v\\n", string(buf))
2.2 Parse 函数
// parse.go
package main
import (
"bytes"
"fmt"
"reflect"
)
func Parse(v interface) ([]byte, error)
var buf bytes.Buffer
if err := parse(&buf, reflect.ValueOf(v)); err != nil
return nil, err
return buf.Bytes(), nil
// parse 是一个内置的递归函数
func parse(buf *bytes.Buffer, v reflect.Value) error
// 取到变量的类型的类别
switch v.Kind()
case reflect.Invalid:
buf.WriteString("null")
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
fmt.Fprintf(buf, "%d", v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
fmt.Fprintf(buf, "%d", v.Uint())
case reflect.String:
fmt.Fprintf(buf, `"%s"`, v.String())
case reflect.Ptr:
// 如果是指针,则解引用后继续解析
return parse(buf, v.Elem())
case reflect.Slice, reflect.Array:
buf.WriteByte('[')
for i := 0; i < v.Len(); i++
if i > 0
buf.WriteString(", ")
if err := parse(buf, v.Index(i)); err != nil
return err
buf.WriteByte(']')
case reflect.Struct:
buf.WriteByte('')
for i := 0; i < v.NumField(); i++
if i > 0
buf.WriteString(", ")
// key 为结构体字段名称
fmt.Fprintf(buf, `"%s":`, v.Type().Field(i).Name)
// value 需要递归解析,因为 value 也需要序列化成 json 串
if err := parse(buf, v.Field(i)); err != nil
return err
buf.WriteByte('')
case reflect.Map:
buf.WriteByte('')
for i, key := range v.MapKeys()
if i > 0
buf.WriteString(", ")
fmt.Fprintf(buf, `"%s":`, key)
if err := parse(buf, v.MapIndex(key)); err != nil
return err
buf.WriteByte('')
default:
return fmt.Errorf("unsupported type: %s", v.Type())
return nil
2.3 编译和运行
> go run main.go parse.go
3. 关于 Elem 方法
func (v Value) Elem() Value
Elem returns the value that the interface v contains or that the pointer v points to. It panics if v’s Kind is not Interface or Ptr. It returns the zero Value if v is nil.
Elem 方法会返回接口 v 或者指针 v 所指向的值(其实就是解引用)。如果 v 的类型不是 Interface 或 Ptr,调用此方法程序就 panic 了(挂了)。如果 v 是 nil 指针,返回对应类型的零值(零值不是数字 0)。
下一篇我们还会遇到这个 Elem 方法。
4. 总结
- 熟练掌握反射解析现实问题
以上是关于083-反射(序列化 json)的主要内容,如果未能解决你的问题,请参考以下文章