我们不同编程语言之间的数据是无法直接交互的,我们想要解决这个问题
就需要将不同语言之间传输的数据做一个统一规范,而json是目前最流行的数据格式
一、json是什么
json 是一种数据交换格式,主要是将要传输的数据转换为json特有的格式
其他人拿到这个json数据后,可以通过数据反向转换得到转换前的数据
这样大家都将数据转为json格式,谁用就拿过来转换为自己识别的数据即可
json数据格式说明
JSON 依赖key-value 键值对形式来保存数据,而“键值对”组合中的键名
写在前面,并且用双引号""包裹,使用冒号:分隔,然后紧跟值
案例 常见格式定义
//json格式key-value
{"firstName": "Json"}
//包含数组的json,value通过方括号包裹
{"address":["北京","上海"]}
//存放多个json,通过逗号分割
[{"name":"tom"},{"haha":"test"}]
当我们不确定这个格式是否正确,可以找个解析的网站去查看
https://www.json.cn/
案例2 当有多个用户信息时逗号分割
[
{"name":"韩梅梅","age":20,"address":["上海","北京"]},
{"name":"李雷","age":26,"address":["山西","北京"]}
]
自动排列效果
[
{
"name":"韩梅梅",
"age":20,
"address":[
"上海",
"北京"
]
},
{
"name":"李雷",
"age":26,
"address":[
"山西",
"北京"
]
}
]
二、json序列化
json序列化是指,将语言中键值对类型的数据转换为json字符串
比如go语言中的结构体、map、数组、切片等,转换为json的格式被称为格式化
登陆标准库文档
https://studygolang.com/pkgdoc
有一个叫 encoding/json 的包是用于处理json字符串的
根据开篇的提示找到Marshal与Unmarshal 函数,他们是用于正反向转换json用的
func Marshal(v interface{}) ([]byte, error)
//通过文档得知,Marshal函数接收的是一个空接口类型
//意思是我们可以将go的任意类型的数据传入进去
//而返回值是一个byte的切片,以及error
1 结构体序列化
package main
import (
"encoding/json"
"fmt"
)
type Monster struct{ //我们先定义一个结构体
Name string
Age int
Birthady string
Sal float64
Skill string
}
func main(){
monster := Monster{ //实例化结构体
Name: "牛魔王",
Age: 500,
Birthady: "2011-11-11",
Sal: 8000.0,
Skill: "牛魔拳",
}
data,err := json.Marshal(&monster) //通过json包下的Marshal函数对结构体进行转换
if err != nil{
fmt.Printf("序列化错误 err=%v",err)
}
//输出序列化的结果
fmt.Printf("monster序列化后=%v\n",string(data))
}
返回
monster序列化后={"Name":"牛魔王","Age":500,"Birthady":"2011-11-11","Sal":8000,"Skill":"牛魔拳"}
2、map序列化
package main
import (
"encoding/json"
"fmt"
)
func testMap(){
//定义一个map
var a map[string]interface{}
a = make(map[string]interface{})
a["name"] = "红孩儿"
a["age"] = 30
a["address"] = "火云洞"
data,err := json.Marshal(a) //将a这个map进行序列化
if err != nil{
fmt.Printf("序列化错误 err=%v",err)
}
fmt.Printf("a map序列化后=%v\n",string(data))
}
func main(){
testMap()
}
返回
a map序列化后={"address":"火云洞","age":30,"name":"红孩儿"}
map类型是无序的,所以结果略有不同
3、切片序列化
package main
import (
"encoding/json"
"fmt"
)
func testSlice(){
var slice []map[string]interface{} //定义一个切片,声明值为空接口
var m1 map[string]interface{}
m1 = make(map[string]interface{})
m1["name"] = "jack"
m1["age"] = "7"
m1["address"] = "北京"
slice = append(slice,m1) //将m1加入切片
var m2 map[string]interface{}
m2 = make(map[string]interface{})
m2["name"] = "tom"
m2["age"] = "20"
m2["address"] = "墨西哥"
slice = append(slice,m2) //将m2加入切片
data,err := json.Marshal(slice) //将切片进行序列化操作
if err != nil{
fmt.Printf("序列化错误 err=%v",err)
}
fmt.Printf("切片序列化后=%v\n",string(data))
}
func main(){
testSlice()
}
返回
切片序列化后=[{"address":"北京","age":"7","name":"jack"},{"address":"墨西哥","age":"20","name":"tom"}]
我们发现他变成一个数组了,因为他里面有多个json,多个json通过逗号分隔
4、基本数据类型 序列化
基本数据类型虽然没有键值对,但是也是可以做序列化的,但是没有意义
package main
import (
"encoding/json"
"fmt"
)
func testFloat64(){
var num1 float64 = 23456.68
//序列化
data,err := json.Marshal(num1)
if err != nil{
fmt.Printf("序列化错误 err=%v",err)
}
fmt.Printf("普通数据类型序列化后=%v\n",string(data))
}
func main(){
testFloat64()
}
5、序列化数组
package main
import (
"encoding/json"
"fmt"
)
func testSlice(){
var slice []map[string]interface{} //定义一个切片,和上面切片类似
var m1 map[string]interface{}
m1 = make(map[string]interface{})
m1["name"] = "jack"
m1["age"] = "7"
m1["address"] = "北京"
slice = append(slice,m1)
var m2 map[string]interface{}
m2 = make(map[string]interface{})
m2["name"] = "tom"
m2["age"] = "20"
m2["address"] = [2]string{"墨西哥","夏威夷"} //我们在某个值中定义为数组
slice = append(slice,m2)
data,err := json.Marshal(slice)
if err != nil{
fmt.Printf("序列化错误 err=%v",err)
}
fmt.Printf("切片序列化后=%v\n",string(data))
}
func main(){
testSlice()
}
返回
切片序列化后=[{"address":"北京","age":"7","name":"jack"},{"address":["墨西哥","夏威夷"],"age":"20","name":"tom"}]
三、标签
我们上面序列化结构体的时候,实际上就是把字段的key作为json的key使用
但在浏览器中我们更希望接收到的是首字母小写的字段,为了不破坏结构体调用,使用标签
package main
import (
"encoding/json"
"fmt"
)
//定义一个结构体
type Monster struct{
Name string `json:"xxx_name"` //我们在定义结构体的时候,设置好tag json的一个标签
Age int `json:"xxx_age"` //这样在转换时,就会转换为小写
Birthady string
Sal float64
Skill string
}
func testStruct(){
monster := Monster{
Name: "牛魔王" ,
Age: 500,
Birthady: "2011-11-11",
Sal: 8000.0,
Skill: "牛魔拳",
}
data,err := json.Marshal(&monster)
if err != nil{
fmt.Printf("序列化错误 err=%v",err)
}
fmt.Printf("monster序列化后=%v\n",string(data))
}
func main(){
testStruct()
}
返回
monster序列化后={"xxx_name":"牛魔王","xxx_age":500,"Birthady":"2011-11-11","Sal":8000,"Skill":"牛魔拳"}
我们在结构体后面添加别名,这样在序列化时导出的就是别名的字段了
我们不能直接将字段定义为小写,这样会影响到其他的包进行调用
四、反序列化
就是将我们上面转换的json,重新转换为我们go所使用的数据类型
func Unmarshal(data []byte, v interface{}) error
//传入的值是一个切片类型
//后面的v interface是指定我们用于接收json数据的结构体
//是否接收成功返回一个error的值
1、json转换为结构体
package main
import (
"encoding/json"
"fmt"
)
//定义一个结构体,必须要有这个结构体才能去反序列化到结构体
type Monster struct{
Name string
Age int
Birthady string
Sal float64
Skill string
}
func unmarshalStruct(){
//我们将这个json写入到变量,不过因为他本身有双引号,这里通过反斜杠将双斜杠转义
//在实际开发中,这个str的字符串是通过前端POST上传或者读取文件得到的
str := "{\"Name\":\"牛魔王\",\"Age\":500,\"Birthady\":\"2011-11-11\",\"Sal\":8000,\"Skill\":\"牛魔拳\"}"
var monster Monster //定义一个结构体,去接收反序列化json的结果
err := json.Unmarshal([]byte(str),&monster) //反序列化,通过[]byte(str)类型断言将str转换为切片
if err != nil{
fmt.Printf("unmarshal err=%v\n",err)
}
fmt.Printf("反序列化后 monster=%v",monster)
}
func main(){
unmarshalStruct()
}
返回
反序列化后 monster={牛魔王 500 2011-11-11 8000 牛魔拳}
2、json转换为map
package main
import (
"encoding/json"
"fmt"
)
func unmarshalMap(){
str := "{\"address\":\"火云洞\",\"age\":30,\"name\":\"红孩儿\"}"
var a map[string]interface{} //定义一个map,必须和要转换的json之前的数据类型相同
//反序列化时,Unmarshal已经封装了make动作,这里就不make了
err := json.Unmarshal([]byte(str),&a) //反序列化,必须传入&a才可使用
if err != nil{
fmt.Printf("unmarshal err=%v\n",err)
}
fmt.Printf("反序列化map后 a=%v",a)
}
func main(){
unmarshalMap()
}
返回
反序列化map后 a=map[address:火云洞 age:30 name:红孩儿]
3、json反序列化成切片
package main
import (
"encoding/json"
"fmt"
)
func unmarshalSclice(){
str := "[{\"address\":\"北京\",\"age\":\"7\",\"name\":\"jack\"}," +
"{\"address\":\"墨西哥\",\"age\":\"20\",\"name\":\"tom\"}]"
var slice []map[string]interface{} //定义一个切片,同样无需序列化
err := json.Unmarshal([]byte(str),&slice)
if err != nil{
fmt.Printf("unmarshal err=%v\n",err)
}
fmt.Printf("反序列化slice后 slice=%v",slice)
}
func main(){
unmarshalSclice()
}
返回
反序列化slice后 slice=[map[address:北京 age:7 name:jack] map[address:墨西哥 age:20 name:tom]]
小结说明
1、在反序列化一个json串时,要确保反序列化后的数据类型和原来序列化前的数据类型一致
2、上面的str是为了测试,在实际中是通过其他程序return过来,或者tcp传输过来的,不需要反斜杠去转义双引号