likes
comments
collection
share

什么是JSON序列化和反序列化?

作者站长头像
站长
· 阅读数 49

什么是JSON序列化和反序列化?

JSON(JavaScript Object Notation)是一种常用的数据交换格式,它使用键值对的方式来表示数据。在Golang中,JSON序列化是将Go语言的结构体或其他类型数据转换为JSON格式的过程,而JSON反序列化是将JSON格式的数据转换为Go语言的结构体或其他类型数据的过程。

JSON序列化原理

Golang中的JSON序列化是通过将结构体或其他类型数据转换为JSON字符串来实现的。Golang的encoding/json包提供了Marshal函数来进行JSON序列化。它使用反射来遍历结构体的字段,并将字段的名称和值转换为JSON键值对。然后,Marshal函数将这些键值对组合成一个JSON字符串。

以下是一个简单的示例,演示了如何将结构体序列化为JSON字符串:

package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
	person := Person{
		Name: "Alice",
		Age:  25,
	}

	jsonData, err := json.Marshal(person)
	if err != nil {
		fmt.Println("JSON serialization failed:", err)
		return
	}

	fmt.Println(string(jsonData))
}

运行以上代码,输出结果为:

{"name":"Alice","age":25}

json.Marshal方法

json.Marshal方法用于将Go语言的结构体或其他类型数据序列化为JSON格式的字节切片。它的函数签名如下:

func Marshal(v interface{}) ([]byte, error)

其中,v表示需要序列化的数据。

Marshal方法使用反射来遍历结构体的字段,并将字段的名称和值转换为JSON键值对。然后,它将这些键值对组合成一个JSON字符串,并返回一个字节切片。

json.Marshal方法源代码解析

json.Marshal方法的源代码位于encoding/json包的marshal.go文件中。以下是对该方法的源代码进行解析:

func Marshal(v interface{}) ([]byte, error) {
	e := newEncodeState()
	err := e.marshal(v, encOpts{escapeHTML: true})
	if err != nil {
		return nil, err
	}
	return append([]byte(nil), e.Bytes()...), nil
}

json.Marshal方法首先创建了一个encodeState结构体,该结构体用于保存编码的状态。然后,它调用了e.marshal方法,将需要序列化的数据和一些编码选项传递给它进行序列化。最后,它将序列化后的字节切片拷贝到一个新的字节切片中,并返回。

e.marshal方法的源代码位于encoding/json/encode.go文件中。以下是对该方法的源代码进行解析:

func (e *encodeState) marshal(v interface{}, opts encOpts) error {
	if v == nil {
		e.WriteByte('n')
		return nil
	}

	rv := reflect.ValueOf(v)
	rt := rv.Type()

	// 处理指针类型
	if rt.Kind() == reflect.Ptr {
		if rv.IsNil() {
			e.WriteByte('n')
			return nil
		}
		rv = rv.Elem()
		rt = rv.Type()
	}

	// 处理基本类型
	switch rt.Kind() {
	case reflect.Bool:
		if rv.Bool() {
			e.WriteString("true")
		} else {
			e.WriteString("false")
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		e.WriteString(strconv.FormatInt(rv.Int(), 10))
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		e.WriteString(strconv.FormatUint(rv.Uint(), 10))
	case reflect.Float32:
		e.WriteString(strconv.FormatFloat(rv.Float(), 'g', -1, 32))
	case reflect.Float64:
		e.WriteString(strconv.FormatFloat(rv.Float(), 'g', -1, 64))
	case reflect.String:
		e.writeString(rv.String(), opts.escapeHTML)
	// 处理复合类型
	case reflect.Array, reflect.Slice:
		if rv.IsNil() {
			e.WriteString("null")
			return nil
		}
		e.WriteByte('[')
		...
		// 处理数组和切片的元素
		...
		e.WriteByte(']')
	case reflect.Struct:
		e.WriteByte('{')
		...
		// 处理结构体字段
		...
		e.WriteByte('}')
	case reflect.Map:
		if rv.IsNil() {
			e.WriteString("null")
			return nil
		}
		e.WriteByte('{')
		...
		// 处理映射的键值对
		...
		e.WriteByte('}')
	default:
		return &UnsupportedTypeError{rt}
	}

	return nil
}

e.marshal方法是一个递归方法,用于将数据编码为JSON格式的字节切片。它首先判断传入的值是否为nil,如果是则写入null到输出中。然后,它使用反射获取值的类型,并根据类型进行不同的处理。

对于基本类型,它使用strconv包将值转换为字符串并写入输出中。对于字符串类型,它调用e.writeString方法将字符串写入输出中,根据opts.escapeHTML选项决定是否转义HTML字符。

对于复合类型,它根据类型的不同进行不同的处理。对于数组和切片,它首先判断是否为nil,如果是则写入null到输出中。然后,它使用循环递归地处理数组或切片的每个元素,并在元素之间添加逗号和空格。

对于结构体,它首先写入{到输出中,然后使用循环递归地处理结构体的每个字段,并在字段之间添加逗号和空格。

对于映射,它首先判断是否为nil,如果是则写入null到输出中。然后,它使用循环递归地处理映射的每个键值对,并在键值对之间添加逗号和空格。

对于其他未支持的类型,它返回一个UnsupportedTypeError错误。

JSON反序列化原理

Golang中的JSON反序列化是通过将JSON字符串转换为结构体或其他类型数据来实现的。encoding/json包提供了Unmarshal函数来进行JSON反序列化。它解析JSON字符串,并将键值对映射到结构体的字段上。

以下是一个简单的示例,演示了如何将JSON字符串反序列化为结构体:

package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
	jsonData := `{"name":"Alice","age":25}`

	var person Person
	err := json.Unmarshal([]byte(jsonData), &person)
	if err != nil {
		fmt.Println("JSON deserialization failed:", err)
		return
	}

	fmt.Println(person.Name, person.Age)
}

运行以上代码,输出结果为:

Alice 25

json.Unmarshal方法

json.Unmarshal方法用于将JSON格式的字节切片反序列化为Go语言的结构体或其他类型数据。它的函数签名如下:

func Unmarshal(data []byte, v interface{}) error

其中,data表示需要反序列化的JSON数据,v表示接收反序列化结果的变量。

Unmarshal方法解析JSON字符串,并将键值对映射到结构体的字段上。它使用反射来根据字段的类型将JSON值转换为对应的Go语言类型。最后,它将反序列化后的数据存储到v变量中。

json.Unmarshal方法源代码解析

json.Unmarshal方法的源代码位于encoding/json包的decode.go文件中。以下是对该方法的源代码进行解析:

func Unmarshal(data []byte, v interface{}) error {
	d := newDecodeState()
	if err := d.unmarshal(data, v); err != nil {
		return err
	}
	return nil
}

json.Unmarshal方法首先创建了一个decodeState结构体,该结构体用于保存解码的状态。然后,它调用了d.unmarshal方法,将需要反序列化的JSON数据和接收结果的变量传递给它进行反序列化。最后,它返回可能出现的错误。

d.unmarshal方法的源代码位于encoding/json/decode.go文件中。以下是对该方法的源代码进行解析:

func (d *decodeState) unmarshal(data []byte, v interface{}, opts decOpts) error {
	// 初始化解码状态
	d.init(data)

	// 解码值
	err := d.value(reflect.ValueOf(v), opts)
	if err != nil {
		return err
	}

	// 确保所有输入都已消耗
	if !d.ok() {
		return &SyntaxError{msg: "unexpected end of JSON input"}
	}

	return nil
}

func (d *decodeState) value(v reflect.Value, opts decOpts) error {
	// 确保传入的值是可写的
	if !v.CanSet() {
		return &InvalidUnmarshalError{v.Type()}
	}

	// 处理指针类型
	if v.Kind() == reflect.Ptr {
		if v.IsNil() {
			v.Set(reflect.New(v.Type().Elem()))
		}
		v = v.Elem()
	}

	// 处理基本类型
	switch v.Kind() {
	case reflect.Bool:
		return d.boolValue(v)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return d.intValue(v)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return d.uintValue(v)
	case reflect.Float32, reflect.Float64:
		return d.floatValue(v)
	case reflect.String:
		return d.stringValue(v, opts)
	// 处理复合类型
	case reflect.Array, reflect.Slice:
		return d.arrayValue(v, opts)
	case reflect.Struct:
		return d.structValue(v, opts)
	case reflect.Map:
		return d.mapValue(v, opts)
	default:
		return &UnsupportedTypeError{v.Type()}
	}
}

// 具体类型的解码方法
...

d.unmarshal方法是解码JSON数据的入口方法。它首先调用d.init方法初始化解码状态,然后调用d.value方法解码值。

d.value方法根据传入值的类型进行不同的处理。对于指针类型,它首先判断指针是否为nil,如果是则创建一个新的指针对象。然后,它递归地处理指针指向的值。

对于基本类型,它调用相应的解码方法,如d.boolValued.intValued.uintValued.floatValued.stringValue。这些解码方法根据JSON数据的类型将其解码为相应的Go类型,并将解码结果赋值给传入的值。

对于复合类型,如数组、切片、结构体和映射,它调用相应的解码方法,如d.arrayValued.structValued.mapValue。这些解码方法根据JSON数据的结构将其解码为相应的Go类型,并将解码结果赋值给传入的值。

对于其他未支持的类型,它返回一个UnsupportedTypeError错误。

JSON序列化和反序列化的高级用法

Golang的encoding/json包还提供了一些高级用法,例如忽略空值字段、自定义字段名称、处理嵌套结构体等。以下是一些示例代码:

  • 忽略空值字段:
type Person struct {
	Name string `json:"name,omitempty"`
	Age  int    `json:"age,omitempty"`
}
  • 自定义字段名称:
type Person struct {
	Name string `json:"fullName"`
	Age  int    `json:"age"`
}
  • 处理嵌套结构体:
type Address struct {
	City  string `json:"city"`
	State string `json:"state"`
}

type Person struct {
	Name    string  `json:"name"`
	Age     int     `json:"age"`
	Address Address `json:"address"`
}

以上示例代码只是JSON序列化和反序列化的一小部分内容。通过深入学习encoding/json包的文档,你可以了解更多高级用法和技巧。

结论

本文介绍了Golang中JSON序列化和反序列化的原理,并提供了一些代码示例。JSON序列化和反序列化是在Golang中处理JSON数据的常见操作,它们可以帮助我们在不同系统之间传递和处理数据。通过掌握这些技术,你可以更好地利用Golang的优势来处理JSON数据。

转载自:https://juejin.cn/post/7310111620369547283
评论
请登录