在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
结构体转JSON JSON格式是一种用途广泛的对象文本格式。在Go语言中,结构体可以通过系统提供的json.Marshal()函数进行序列化。为了演示怎么样通过反射获取结构体成员以及各种值的过程,下面使用反射将结构体序列化为文本数据。 1.数据结构及入口函数 将结构体序列化为JSON的步骤如下:
参见下面的代码。序列化JSON主流程: func main() { // 声明技能结构 type Skill struct { Name string Level int } // 声明角色结构 type Actor struct { Name string Age int Skills []Skill } // 填充基本角色数据 a := Actor{ Name: "cow boy", Age: 37, Skills: []Skill{ {Name: "Roll and roll", Level: 1}, {Name: "Flash your dog eye", Level: 2}, {Name: "Time to have Lunch", Level: 3}, }, } if result, err := MarshalJson(a); err == nil { fmt.Println(result) } else { fmt.Println(err) } }
完成代码输出如下: {"Name":"cow boy","Age":37,"Skills":[{"Name":"Roll and roll","Level":1},{"Name":"Flash your dog eye","Level":2},{"Name":"Time to have Lunch","Level":3}]}
2.序列化主函数 MarshalJson()是序列化过程的主要函数入口,通过这个函数会调用不同类型的子序列化函数。MarshalJson()函数传入一个interface{}的数据,并将这个数据转换为JSON字符串返回,如果发生错误,则返回错误信息。 序列化JSON主函数: // 给外部使用序列化值为JSON的接口 func MarshalJson(v interface{}) (string, error) { // 准备一个缓冲 var b bytes.Buffer // 将任意值转换为json并输出到缓冲 if err := writeAny(&b, reflect.ValueOf(v)); err == nil { return b.String(), nil } else { return "", err } }
代码说明如下:
MarshalJson()这个函数其实是对writeAny()函数的一个封装,将外部的interface{}类型转换为内部的reflect.Value类型,同时构建输出缓冲,将一些复杂的操作简化,方便外部使用。 3.任意值序列化 writeAny()函数传入一个字节缓冲和反射值对象,将反射值对象转换为JSON格式并写入字节缓冲中。参见下面的代码: 任意值序列化: // 将任意值转换为json并输出到缓冲 func writeAny(buff *bytes.Buffer, value reflect.Value) error { switch value.Kind() { case reflect.String: // 写入带有双引号括起来的字符串 buff.WriteString(strconv.Quote(value.String())) case reflect.Int: // 将整形转换为字符串并写入缓冲 buff.WriteString(strconv.FormatInt(value.Int(), 10)) case reflect.Slice: return writeSlice(buff, value) case reflect.Struct: return writeStruct(buff, value) default: // 遇到不认识的种类,返回错误 return errors.New("unsupport kind: " + value.Kind().String()) } return nil }
代码说明如下:
writeAny()函数是整个序列化中非常重要的环节,可以通过扩充switch中的种类扩充序列化能识别的类型。 4.切片序列化 writeAny()函数中会调用writeSlice()函数将切片类型转换为JSON格式的字符串并将数据写入缓冲中。参见下面的代码。 切片序列化: // 将切片转换为json并输出到缓冲 func writeSlice(buff *bytes.Buffer, value reflect.Value) error { // 写入切片开始标记 buff.WriteString("[") // 遍历每个切片元素 for s := 0; s < value.Len(); s++ { sliceValue := value.Index(s) // 写入每个切片元素 writeAny(buff, sliceValue) // 写入每个元素尾部逗号,最后一个字段不添加 if s < value.Len()-1 { buff.WriteString(",") } } // 写入切片结束标记 buff.WriteString("]") return nil }
代码说明如下:
由于writeAny的功能较为完善,因此序列化切片只需要添加头尾标识符及元素分隔符就可以了。 5.结构体序列化 在JSON格式中,切片是一系列值的序列,以方括号开头和结尾;结构体由键值对组成,以大括号开始和结束。两种结构的元素均以逗号分隔。序列化结构体的过程参见下面的代码。 结构体序列化: // 将结构体序列化为json并输出到缓冲 func writeStruct(buff *bytes.Buffer, value reflect.Value) error { // 取值的类型对象 valueType := value.Type() // 写入结构体左大括号 buff.WriteString("{") // 遍历结构体的所有值 for i := 0; i < value.NumField(); i++ { // 获取每个字段的字段值(reflect.Value) fieldValue := value.Field(i) // 获取每个字段的类型(reflect.StructField) fieldType := valueType.Field(i) // 写入字段名左双引号 buff.WriteString("\"") // 写入字段名 buff.WriteString(fieldType.Name) // 写入字段名右双引号和冒号 buff.WriteString("\":") // 写入每个字段值 writeAny(buff, fieldValue) // 写入每个字段尾部逗号,最后一个字段不添加 if i < value.NumField()-1 { buff.WriteString(",") } } // 写入结构体右大括号 buff.WriteString("}") return nil }
代码说明如下:
6.总结
|
请发表评论