什么是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字符串:

go 复制代码
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))
}

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

json 复制代码
{"name":"Alice","age":25}

json.Marshal方法

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

go 复制代码
func Marshal(v interface{}) ([]byte, error)

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

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

json.Marshal方法源代码解析

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

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文件中。以下是对该方法的源代码进行解析:

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字符串反序列化为结构体:

go 复制代码
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语言的结构体或其他类型数据。它的函数签名如下:

go 复制代码
func Unmarshal(data []byte, v interface{}) error

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

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

json.Unmarshal方法源代码解析

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

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文件中。以下是对该方法的源代码进行解析:

kotlin 复制代码
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包还提供了一些高级用法,例如忽略空值字段、自定义字段名称、处理嵌套结构体等。以下是一些示例代码:

  • 忽略空值字段:
go 复制代码
type Person struct {
	Name string `json:"name,omitempty"`
	Age  int    `json:"age,omitempty"`
}
  • 自定义字段名称:
go 复制代码
type Person struct {
	Name string `json:"fullName"`
	Age  int    `json:"age"`
}
  • 处理嵌套结构体:
go 复制代码
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数据。

相关推荐
红尘散仙11 分钟前
我把终端小说阅读器接上了 AI Agent:TRNovel 现在能用 skill 生成书源了
人工智能·后端·rust
卷毛的技术笔记2 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
会编程的土豆2 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
喵个咪2 小时前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
basketball6163 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
qq_2518364573 小时前
SpringBoot+Vue 共享电池柜管理系统 完整实现 前后端分离项目实战 完整代码
vue.js·spring boot·后端
zhangxingchao3 小时前
AI 大模型核心六:量化、Workflow 与 Agent、多轮 RAG
前端·人工智能·后端
IT_陈寒4 小时前
Vite打包时遇到的坑,原来问题出在这里
前端·人工智能·后端
ayqy贾杰5 小时前
基层管理的三板斧,在AI时代行不通了
前端·后端·团队管理
Apifox5 小时前
Apifox 5 月更新|Postman 导入优化、Runner 支持非 root 运行、请求代码自动带鉴权
前端·后端·安全