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)的主要内容,如果未能解决你的问题,请参考以下文章

反射和序列化

Json.NET 是不是缓存类型的序列化信息?

Go反射

PHP中JSON的应用

Go_Json序列化

go学习-反射