Go语言中JSON的使用
JSON 格式是一种用途广泛的对象文本格式。 Go 语言中, 结构体可以通过系统提供的 json.Marshal() 函数进行序列化。
1. 数据结构及入口函数
将结构体序列化为JSON的步骤如下:
a. 准备数据结构体
b. 准备要序列化的结构体数据
c. 调用序列化函数
参见下面的代码:
type ColorGroup struct {
ID int
Name string
Colors []string
}
func marshalTest() {
// 实例化结构体并初始化
group := ColorGroup{
ID: 1,
Name: "Reds",
Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
}
b, err := json.Marshal(group) //调用json.Marshal序列化为字节数组
if err != nil {
fmt.Println("error:", err)
}
os.Stdout.Write(b)
os.Stdout.WriteString("\n")
}
func main() {
marshalTest()
}
输出内容如下:
{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
2. 序列化主函数
MarshalJson()是序列化过程的主要函数入口,通过这个函数会调用不同类型的子序列化函数。
如下代码所示:
func MarshalJson(v interface{}) (string, error) {
var b bytes.Buffer //准备一个缓冲,在大量字符串连接时,推荐使用这个结构。
if err := writeAny(&b, reflect.ValueOf(v)); err == nil {
return b.String(), nil
} else {
return "", nil
}
}
MarshalJson()这个函数其实是对 writeAny ()函数的一个封装,将外部的 interface{}类型转换为内部的 reflect. Value 类型,同时构建输出缓冲,将一些复杂的操作简化,方便外部使用。
3. 任意值序列化
writeAny()函数传入一字节缓冲和反射值对象,将反射值对象转换为 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
}
## 4. 切片序列化
writeAny()函数中会调用 writeSlice ()函数将切片类型转换为 JSON 格式的字符串并将数据写入缓冲中。参见下面的代码。
```go
// 将切片转为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
}
5. 结构体序列化
结构体由键值对组成,以大括号开始和结束。元素均以逗号分隔。序列化结构体的过程参见下面的代码。
// 将结构体序列化为JSON格式并输出到缓冲中
func WriteStruct(buff *bytes.Buffer, value reflect.Value) error {
valueType := value.Type()
buff.WriteString("{") //写入结构体左大括号
for i := 0; i < value.NumField(); i++ { // 遍历结构体的元素
fieldValue := value.Field(i) //获取每个字段的字段值
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. 总结
上面例子只支持整型、字符串、切片和结构体类型序列化为 JSON 格式。如果需要扩充类型,可以在 writeAny()函数中添加。程序功能和结构上还有些不足,例如:
- 没有处理各种异常情况,切片或结构体为空时应该提前判断,否则会触发宕机
- 可以支持结构体标签( Struct Tag ),方便 自定义 JSON 的键名及忽略某些字段的序列化过程,避免这些字段被序列化到 JSON
- 支持缩进且可以自定义缩进字符,将 JSON 序列化后的内容格式化,方便查看。
- 默认应该序列化为[]byte 字节数组,外部自己转换为字符串。在大部分的使用中,JSON 一般 字节数组方式解析、存储、传输,很少以字符串方式解析,因此避免字节数组和字符串的转换可 以提高一些性能。