什么是JSON序列化和反序列化?
什么是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.boolValue
、d.intValue
、d.uintValue
、d.floatValue
和d.stringValue
。这些解码方法根据JSON数据的类型将其解码为相应的Go类型,并将解码结果赋值给传入的值。
对于复合类型,如数组、切片、结构体和映射,它调用相应的解码方法,如d.arrayValue
、d.structValue
和d.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