在处理 JSON 的时候,序列化是一个非常有用的功能,可以直接将对象转换为序列化后的 JSON。
一般来说,处理 JSON 的序列化时的步骤是递归遍历一个对象中的所有字段,然后根据其数据类型生成指定格式的 JSON 数据。
方案Go 语言中,主要有如下数据的类型:
- 数字类型:Int、Float32等等
- 逻辑类型:Bool
- 字符串类型:String
- 数组类型:Array、Slice
- 字典类型:Map
- 自定义数据类型:Struct
- 以上数据类型的指针
因此只需要根据以上类型分别进行处理即可,而遍历对象中的所有字段可以通过反射来完成。
实现首先我们需要实现一些工具函数。
字符串转义
在生成 JSON 字符串时,需要将换行以及双引号转义,以免引起冲突。
func esacpeString(s string) string {
// 将 \n \r " 转义
return strings.ReplaceAll(
strings.ReplaceAll(strings.ReplaceAll(s, "\n", "\\n"), "\r", "\\r"), "\"", "\\\"")
}
忽略类型
有一些类型是不需要生成 JSON 的,例如函数类型,因此编写这样一个函数用来判断类型并返回是否跳过序列化。
func skipParsing(kind reflect.Kind) bool {
switch kind {
case reflect.Chan, reflect.Complex128, reflect.Complex64, reflect.Func, reflect.Invalid:
return true
default:
return false
}
}
序列化器主体
现在开始实现序列化器。
首先,如果数据是 null,则需要直接返回 null 作为序列化结果:
if v == nil {
return "null", nil
}
然后需要检测类型并判断是否需要调过:
t := reflect.TypeOf(v)
kind := t.Kind()
if skipParsing(kind) {
return "", nil
}
switch
数字和逻辑类型
直接格式化返回即可。
case reflect.Int, reflect.Int8, reflect.Int16,
reflect.Int32, reflect.Int64, reflect.Uint,
reflect.Uint8, reflect.Uint16, reflect.Uint32,
reflect.Uint64, reflect.Float32,
reflect.Float64, reflect.Bool:
return fmt.Sprintf("%v", v), nil
字符串类型
返回转义后的字符串。
case reflect.String:
return "\"" + esacpeString(v.(string)) + "\"", nil
数组类型
数组类型稍微复杂一些,需要新建一个数组用来保存每个元素序列化后的数据,然后对数组中的每一个元素单独进行序列化。
case reflect.Array, reflect.Slice:
vArray := reflect.ValueOf(v)
len := vArray.Len()
results := make([]string, len)
// 对数组中每个元素单独进行序列化
for i := 0; i < len; i++ {
result, err := parseJSON(vArray.Index(i).Interface())
if err != nil {
return "", err
}
results[i] = result
}
// 最后用 "," 将每个结果连接起来
return fmt.Sprintf("[%v]", strings.Join(results, ",")), nil
字典类型
字典类型需要遍历所有的 key,将 key 作为 JSON 的 key,再将 value 进行一次序列化。
case reflect.Map:
vMap := reflect.ValueOf(v)
// 获取所有的 key
keys := vMap.MapKeys()
len := len(keys)
results := make([]string, len)
j := 0
// 遍历每一个 key 对应的元素
for i, key := range keys {
val := vMap.MapIndex(key)
result := "null"
// 如果 value 有效
if val.IsValid() {
if !val.CanInterface() {
j++
continue
}
// 对 value 执行序列化操作
r, err := parseJSON(val.Interface())
if err != nil {
return "", err
}
result = r
}
// 使用 "key": value 的格式拼接单个结果
results[i-j] = "\"" + esacpeString(fmt.Sprintf("%v", key)) + "\"" + ":" + result
}
// 最后将所有结果通过 "," 连接
return fmt.Sprintf("{%v}", strings.Join(results[0:len-j], ",")), nil
自定义结构类型
mytag
case reflect.Struct:
value := reflect.ValueOf(v)
num := value.NumField()
results := make([]string, num)
j := 0
// 遍历每一个字段
for i := 0; i < num; i++ {
vField := value.Field(i)
tField := value.Type().Field(i)
// 判断是否有效
if !vField.CanInterface() || skipParsing(tField.Type.Kind()) {
j++
continue
}
// 获取字段名称和 tag 名称
key := tField.Name
tag := tField.Tag.Get("mytag")
if tag != "" {
key = tag
}
// 对值进行序列化
result := "null"
f := vField.Interface()
val := reflect.ValueOf(f)
if val.IsValid() {
r, err := parseJSON(val.Interface())
if err != nil {
return "", err
}
result = r
}
// 使用 "name": value 的格式拼接结果
results[i-j] = "\"" + esacpeString(key) + "\"" + ":" + result
}
// 最后将所有结果通过 "," 连接
return fmt.Sprintf("{%v}", strings.Join(results[0:num-j], ",")), nil
指针类型
指针类型较为简单,只需要将其解引用后对实际数据进行序列化即可。
case reflect.Ptr, reflect.Interface:
val := reflect.ValueOf(v).Elem()
// 判断是否有效
if !val.IsValid() {
return "null", nil
}
// 序列化解引用后的值
return parseJSON(val.Interface())
}
至此所有序列化功能就编写结束了
API
最后,提供两个 API 允许用户调用。
// JSONMarshal 将对象序列化为 JSON 字节流
func JSONMarshal(v interface{}) ([]byte, error) {
result, err := parseJSON(v)
if err != nil {
return nil, err
}
return []byte(result), nil
}
// JSONMarshalAsString 将对象序列化为 JSON 字符串
func JSONMarshalAsString(v interface{}) (string, error) {
result, err := parseJSON(v)
return result, err
}
使用
安装
go get -u gitee.com/hez2010/hjson
调用
package main
import (
"fmt"
// 引入库
"gitee.com/hez2010/hjson"
)
// 声明一个结构
type example struct {
A map[string]int `mytag:"map"` // 使用 mytag 指定名称
B bool `mytag:"bool"`
C int `mytag:"int"`
D string `mytag:"string"`
E *bool // 指针类型也支持
}
func main() {
m := map[string]int{
"A": 1,
}
e := example{m, false, 1, "hello", nil}
// 序列化为字符串
result, _ := hjson.JSONMarshalAsString(e)
fmt.Println(result)
}
输出:
{"map":{"A":1},"bool":false,"int":1,"string":"hello","E":null}
mytag
测试
// 测试数组
func TestArray(t *testing.T) {
expected := "[1,2,3,4,5]"
result, err := parseJSON([]int{1, 2, 3, 4, 5})
if err != nil {
t.Error(err)
}
if result != expected {
t.Errorf("expected: %v, actual: %v", expected, result)
}
}
// 测试数字
func TestNumber(t *testing.T) {
expected := "6"
result, err := parseJSON(6)
if err != nil {
t.Error(err)
}
if result != expected {
t.Errorf("expected: %v, actual: %v", expected, result)
}
}
// 测试字符串
func TestString(t *testing.T) {
expected := "\"hell\\no\""
result, err := parseJSON("hell\no")
if err != nil {
t.Error(err)
}
if result != expected {
t.Errorf("expected: %v, actual: %v", expected, result)
}
}
// 测试布尔值
func TestBool(t *testing.T) {
expected := "true"
result, err := parseJSON(true)
if err != nil {
t.Error(err)
}
if result != expected {
t.Errorf("expected: %v, actual: %v", expected, result)
}
}
// 测试 null
func TestNil(t *testing.T) {
expected := "null"
result, err := parseJSON(nil)
if err != nil {
t.Error(err)
}
if result != expected {
t.Errorf("expected: %v, actual: %v", expected, result)
}
}
// 测试字典
func TestMap(t *testing.T) {
expected1 := `{"a":1,"b":2}`
expected2 := `{"b":2,"a":1}`
result, err := parseJSON(map[string]int{"a": 1, "b": 2})
if err != nil {
t.Error(err)
}
if result != expected1 && result != expected2 {
t.Errorf("expected: %v, actual: %v", expected1, result)
}
}
// 测试指针
func TestPointer(t *testing.T) {
expected1 := `{"a":1,"b":2}`
expected2 := `{"b":2,"a":1}`
result, err := parseJSON(&map[string]int{"a": 1, "b": 2})
if err != nil {
t.Error(err)
}
if result != expected1 && result != expected2 {
t.Errorf("expected: %v, actual: %v", expected1, result)
}
}
type structTest struct {
A string
B int
C map[string]int
D bool
E *structTest
F *structTest
}
// 测试自定义的复杂结构
func TestStruct(t *testing.T) {
s1 := structTest{"hello", 1, map[string]int{"a": 1, "b": 2}, false, nil, nil}
s2 := structTest{"hello", 1, map[string]int{"a": 1, "b": 2}, false, nil, &s1}
expected1 := `{"A":"hello","B":1,"C":{"a":1,"b":2},"D":false,"E":null,"F":{"A":"hello","B":1,"C":{"a":1,"b":2},"D":false,"E":null,"F":null}}`
expected2 := `{"A":"hello","B":1,"C":{"b":2,"a":1},"D":false,"E":null,"F":{"A":"hello","B":1,"C":{"a":1,"b":2},"D":false,"E":null,"F":null}}`
expected3 := `{"A":"hello","B":1,"C":{"a":1,"b":2},"D":false,"E":null,"F":{"A":"hello","B":1,"C":{"b":2,"a":1},"D":false,"E":null,"F":null}}`
expected4 := `{"A":"hello","B":1,"C":{"b":2,"a":1},"D":false,"E":null,"F":{"A":"hello","B":1,"C":{"b":2,"a":1},"D":false,"E":null,"F":null}}`
result, err := parseJSON(s2)
if err != nil {
t.Error(err)
}
if result != expected1 && result != expected2 && result != expected3 && result != expected4 {
t.Errorf("expected: %v, actual: %v", expected1, result)
}
}
type tagTest struct {
A string `mytag:"tag1"`
B int `mytag:"tag2"`
}
// 测试自定义 Tag
func TestTag(t *testing.T) {
s := tagTest{"hello", 1}
expected := `{"tag1":"hello","tag2":1}`
result, err := parseJSON(s)
if err != nil {
t.Error(err)
}
if result != expected {
t.Errorf("expected: %v, actual: %v", expected, result)
}
}
最后运行测试:
go test
得到如下输出:
PASS
ok gitee.com/hez2010/hjson 0.635s
所有测试全部通过了。
开源和贡献
最后,所有代码放到 https://gitee.com/hez2010/hjson 并以 MIT 协议开源,欢迎各位 star、使用和贡献代码。