JSON
JSON是一种轻量级的数据交换格式,易于编写也易于机器解析和生成。
JSON格式能有效提高网络传输效率,在传输时能够将程序中的数据类型序列化为json字符串,在接收方反序列化恢复原来的数据格式。
JSON是一种键值对的格式,如下:
{
"name":"小许",
"sex" : "男",
"age" : 18,
"address":["北京","天津","河北"]
}
JSON简便的格式有高速的传输是数据在传输时的主要格式,基本上编程语言都支持编程语言内置结构的数据向json字符串转化。
序列化
在不同语言中有不同的方式,下面将介绍Java与Go语言的序列化与反序列化。在Java中一切皆对象,所以序列化非常容易,直接继承java.io.Serializable
。一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。
public class Person implements java.io.Serializable{
private int id;
private String name;
private String sex;
private String address;
/**
省略setter和getter
*/
}
继承该对象后就可以调用如下函数序列化Java对象
public final void writeObject(Object x) throws IOException
并通过如下函数反序列化
public final Object readObject() throws IOException, ClassNotFoundException
Go语言对JSON操作的包均在encoding/json
包下,包下提供了Marshal
方法来序列化Go内置对象。Marshal函数返回v的json编码。
func Marshal(v interface{}) ([]byte, error)
//创建结构体
type Person struct {
name string
address string
}
//序列化
per := Person{
name: "钢铁侠",
address: "漫威",
}
perjson, err := json.Marshal(&per)
if err != nil {
panic(err)
}
fmt.Printf("per序列化的json数据为为:%v", perjson)
fmt.Printf("per序列化的json字符串为:%v", string(perjson))
上面代码中序列化了一个Person对象,但打印时却打印失败,这是由于结构体成员都是小写的,只能在本包内访问,序列化没有意义,也不支持私有类的序列化。
type Person struct {
Name string
Address string
}
将结构体改为公共类时就可以序列化了。
不同于Java一切皆对象的性质,Go中除了结构体还有map,数组,切片等。
//定义一个map类型
func initMap() map[string]string {
var tmp map[string]string = map[string]string{}
tmp["1"] = "aaa"
tmp["2"] = "bbb"
tmp["3"] = "ccc"
return tmp
}
//对map序列化
a := initMap()
ajson, err := json.Marshal(&a)
if err != nil {
panic("ajson序列化失败")
}
fmt.Printf("%v\n", ajson)
fmt.Println(string(ajson))
//输出序列化时的数据类型
fmt.Printf("%T\n", ajson)
fmt.Printf("%T\n", string(ajson))
可以看出序列化后时一个字节数组,通过string()函数将其转化为字符串,即为json字符串。
Go序列化时的tag标签的使用
在序列化时成员变量的名称均是首字母大写,然而在实际使用时需要统一书写规范,需要特定的名称,这就需要使用序列化时的tag标签。
{"Name":"钢铁侠","Address":"漫威"}
Go语言结构体序列化tag
使用规则是在结构体字段后添加json:"name"
并用反引号包裹。
type Person struct {
Name string `json:"person_name"`
Address string `json:"person_address"`
Age int
}
在Name和Address使用了tag那么其序列化后的名称会变为自定义的person_name,Age成员没有使用tag因此不会改变。
per := Person{
Name: "钢铁侠",
Address: "漫威",
Age: 18,
}
perjson, err := json.Marshal(&per)
if err != nil {
panic(err)
}
fmt.Printf("per序列化的json数据为为:%v", perjson)
fmt.Printf("per序列化的json字符串为:%v", string(perjson))
struct的tag只会在序列化时起作用不会影响结构体的使用。
反序列化
反序列化就是将序列化后的json字符串或者字节数组在转化为编程语言的内置数据结构。这个反序列化可以发生正在同一语言内,也可以发生在在不同语言中,例如,Java类序列化后网络传输到前端由json反序列化,或者其他任何后端语言将内置数据序列化后传输到前端。
在同一语言中对序列化的数据反序列化,Java实现方式与Go实现方式。
/**
go语言实现
*/
//定义结构体
type Person struct {
Name string `json:"person_name"`
Address string `json:"person_address"`
Age int `json:"person_age"`
}
//序列化
per := Person{
Name: "钢铁侠",
Address: "漫威",
Age: 18,
}
perjson, err := json.Marshal(&per)
if err != nil {
panic(err)
}
fmt.Printf("per序列化的json数据为为:%v", perjson)
fmt.Printf("per序列化的json字符串为:%v", string(perjson))
序列化后为一个字节数组,可以通过string()
方法转化为字符串。
func json.Unmarshal(data []byte, v any) error
该方法时反序列化方法,第一个参数为需要反序列化的字节数组,第二个参数为反序列化后数据的赋值变量。
//反序列化赋值变量
var per1 Person
err1 := json.Unmarshal(perjson, &per1)
if err1 != nil {
panic(err1)
}
fmt.Printf("反序列化的数据per1%s,%s,%d", per1.Name, per1.Address, per1.Age)
str := `{"person_name":"钢铁侠","person_address":"漫威","person_age":18}`
var per2 Person
err2 := json.Unmarshal([]byte(str), &per2)
if err2 != nil {
panic(err2)
}
fmt.Println(per2)
对字符串也可以反序列化,需要将字符串转化为字节数组,通过[]byte()
方法。
/**
Java实现
*/
//定义类实现序列化接口java.io.Serializable
public class Person implements java.io.Serializable
{
public String name;
public String address;
public int id;
/*
getter
setter
toString
...
*/
}
//实例化类并序列化类
Person per = new Person();
per.name = "小许";
per.address = "中国";
per.id = 111111;
FileOutputStream fs = new FileOutputStream("./tmp/per.txt");
ObjectOutputStream out = new ObjectOutputStream(fs);
out.writeObject(e);
out.close();
fs.close();
System.out.printf("data is saved in ./tmp/per.txt");
//反序列化
Person e = null;
FileInputStream fileIn = new FileInputStream("./tmp/Person.txt");
ObjectInputStream in = new ObjectInputStream(fileIn);
e = (Person) in.readObject();
in.close();
fileIn.close();
System.out.println("反序列化的对象",e);
System.out.println("name: " + e.name);
System.out.println("address: " + e.address);
System.out.println("id: " + e.id);
这个序列化的字节数组直接存储在文件中,一般情况下都直接网络传输如http协议传输到浏览器端,由浏览器内置解析器即js反序列化。
在前后端交互的过程中主要的数据传输方式就是json字符串串,一般将后端语言的内置对象及数据结构都序列化为json字符串,再由Javascript的JSON,parse()
方法将json字符串转化为js的内置对象。
JS中内置了json字符串与json数据的转化方法,因此只需要再后端语言中将内置数据序列化为json字符串即可,这个过程是统一的,因此诞生了很多序列化工具,仅需少量的代码即可序列化,如阿里巴巴的fastjson
,apache的jackson
等。