原文链接:https://xiets.blog.csdn.net/article/details/130866967
版权声明:原创文章禁止转载
专栏目录:Golang 专栏(总目录)
Go 内置的 encoding/json 包实现了 RFC 7159 中定义的 JSON 的编码和解码。JSON 和 Go 数据类型之间的映射在 json.Marshal() 和 json.Unmarshal() 函数的文档中进行了描述。有关 JSON 和 Go 的详细介绍,可以参考:https://go.dev/blog/json。
1. JSON 的简单 编码 和 解码
encoding/json
包中 JSON 编码和解码相关函数:
// 编码: Go -> JSON
// 把任意 Go 的数据类型编码为 JSON, 返回 []byte形式的JSON 和 错误
func Marshal(v any) ([]byte, error)
// 解码: JSON -> GO
// 把 []byte形式的JSON 解码保存到 Go数量类型v
func Unmarshal(data []byte, v any) error
// 格式/缩进JSON: 把 JSON(src) 使用 indent 作为缩进字符串, 并添加 prefix 前缀, 把结果保存到 dst
func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error
// 编码后格式化/缩进 JSON, 使用 indent 作为缩进字符串, 并添加 prefix 前缀
func MarshalIndent(v any, prefix, indent string) ([]byte, error)
// 压缩JSON: 把 JSON(src) 中可以省略的 空格、TAB、换行符 删掉, 把结果保存到 dst
func Compact(dst *bytes.Buffer, src []byte) error
// HTML字符转换: 把 JSON(src) 中 <、>、&、U+2028 和 U+2029 字符
// 更改为 \u003c、\u003e、\u0026、\u2028、\u2029, 以便 JSON 安全地嵌入 HTML <script> 标签
func HTMLEscape(dst *bytes.Buffer, src []byte)
// 判断 data 是否是 有效的 JSON 格式
func Valid(data []byte) bool
使用 json.Marshal(v any) ([]byte, error)
和 json.Unmarshal(data []byte, v any) error
函数对 JSON 进行直接 编码 和 解码。
代码示例:
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string
Age int
Salary float64
IsMale bool
}
func main() {
p1 := Person{"Hello 世界", 25, 8888.88, true}
// 把 结构体对象(变量或指针均可) 编码为 JSON, 返回 []byte 和 错误
jsonBytes, _ := json.Marshal(p1)
fmt.Println(string(jsonBytes)) // {"Name":"Hello 世界","Age":25,"Salary":8888.88,"IsMale":true}
// 把 JSON 解码到 指针p2 指向的变量中 (必须传指针, 不能传普通变量)
p2 := &Person{}
_ = json.Unmarshal(jsonBytes, p2)
fmt.Printf("%#v\n", *p2) // main.Person{Name:"Hello 世界", Age:25, Salary:8888.88, IsMale:true}
// 编码JSON, 并格式化, 加上 "=" 前缀, 使用 "---" 缩进
jsonBytes, _ = json.MarshalIndent(p1, "=", "---")
fmt.Println(string(jsonBytes))
// {
// =---"Name": "Hello 世界",
// =---"Age": 25,
// =---"Salary": 8888.88,
// =---"IsMale": true
// =}
}
上面示例中,JSON 的字段名默认保持与结构体字段名一致,而 Go 的可导出字段以大写字母开头。可以在 Go 结构体字段后面添加对转换为 JSON 字段时重命名的字符串标签(tag),Go 将通过反射的形式读取标签值完成对字段的读写:
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Salary float64 `json:"salary"`
IsMale bool `json:"is_male"`
}
func main() {
p1 := Person{"Hello 世界", 25, 8888.88, true}
// 把 结构体对象(变量或指针均可) 编码为 JSON, 返回 []byte 和 错误
jsonBytes, _ := json.Marshal(p1)
fmt.Println(string(jsonBytes)) // {"name":"Hello 世界","age":25,"salary":8888.88,"is_male":true}
// 把 JSON 解码到 指针p2 指向的变量中 (必须传指针, 不能传普通变量)
p2 := &Person{}
_ = json.Unmarshal(jsonBytes, p2)
fmt.Printf("%#v\n", *p2) // main.Person{Name:"Hello 世界", Age:25, Salary:8888.88, IsMale:true}
}
2. 解码任意数据(Go 通用的 JSON 模型)
json
包默认使用 map[string]interface{}
和 []interface{}
来存储 JSON 的 对象 和 数组。在不知道 JSON 数据具体结构的情况下,可以将任何 JSON 解码为一个 interface{}
值,然后通过类型断言来获取具体指。
JSON 数据类型默认对应的 Go 类型为:
JSON 类型 | Go 类型 |
---|---|
string | string |
number | float64 |
boolean | bool |
null | nil |
object | map[string]interface{} |
array | []interface{} |
代码示例:
package main
import (
"encoding/json"
"fmt"
)
func main() {
// JSON
jsonString := `{"name":"Hello 世界","age":25,"salary":8888.88,"is_male":true,"parents":["Hello","World"]}`
// 解码 JSON, 把结果赋值给 接口j
var j interface{}
_ = json.Unmarshal([]byte(jsonString), &j)
fmt.Printf("%#v\n", j) // map[age:25 is_male:true name:Hello 世界 parents:[Hello World] salary:8888.88]
/* 解码后 p 的结构:
j = map[string]interface{}{
"name": "Hello 世界",
"age": 25,
"salary": 8888.88,
"is_male": true,
"parents": []interface{}{
"Hello",
"World",
},
}
*/
// 通过断言获取指定字段的值
if jsonObj, ok := j.(map[string]interface{}); ok {
if age, ok := jsonObj["age"]; ok {
if ageFloat, ok := age.(float64); ok {
fmt.Printf("age = %f\n", ageFloat)
}
}
}
// 使用 map[string]interface{} 和 []interface{} 手动构造任何 JSON 结构, 然后编码为 JSON
m := map[string]interface{}{
"name": "Hello 世界",
"age": 25,
"salary": 8888.88,
"is_male": true,
"parents": []interface{}{
"Hello",
"World",
},
}
jm, _ := json.Marshal(m)
fmt.Println(string(jm))
// {"age":25,"is_male":true,"name":"Hello 世界","parents":["Hello","World"],"salary":8888.88}
}
3. 流式 编码器 和 解码器
json
包提供了 json.Decoder
和 json.Encoder
类型来支持流式读取和写入 JSON 数据的常见操作。
json
包中创建 Decoder
和 Encoder
的函数:
// 创建 JSON 解码器, 从 r 中读取 JSON数据
func NewDecoder(r io.Reader) *Decoder
// 创建 JSON 编码器, 把编码结果写到 w
func NewEncoder(w io.Writer) *Encoder
代码示例:
package main
import (
"bytes"
"encoding/json"
"fmt"
"strings"
)
func main() {
jsonString := `{"name":"Hello 世界","age":25,"salary":8888.88,"is_male":true}`
reader := strings.NewReader(jsonString)
var j interface{}
// 创建解码器, 从 reader 中读取 JSON 数据
decoder := json.NewDecoder(reader)
// 把解码结果保存到 j
_ = decoder.Decode(&j)
fmt.Printf("%#v\n", j)
// map[string]interface {}{"age":25, "is_male":true, "name":"Hello 世界", "salary":8888.88}
writer := bytes.NewBuffer(nil)
// 创建编码器, 把编码结果写到 writer
encoder := json.NewEncoder(writer)
// encoder.SetEscapeHTML(true)
// encoder.SetIndent(prefix, indent)
// 编码 j
_ = encoder.Encode(j)
// 获取编码结果
fmt.Printf("%s\n", writer.String())
// {"age":25,"is_male":true,"name":"Hello 世界","salary":8888.88}
}