Files
rikako-note/spring/json/gson/gson.md
2022-10-12 19:50:23 +08:00

178 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

- [gson](#gson)
- [gson简介](#gson简介)
- [gson使用](#gson使用)
- [Gson库通过Maven引入](#gson库通过maven引入)
- [基本类型的序列化和反序列化](#基本类型的序列化和反序列化)
- [对象的序列化和反序列化](#对象的序列化和反序列化)
- [gson和对象联用的使用规范](#gson和对象联用的使用规范)
- [gson和嵌套类的关联使用](#gson和嵌套类的关联使用)
- [gson和数组的关联使用](#gson和数组的关联使用)
- [gson对java中的集合进行序列化和反序列化](#gson对java中的集合进行序列化和反序列化)
- [gson对Map类型的序列化和反序列化](#gson对map类型的序列化和反序列化)
- [序列化和反序列化泛型对象](#序列化和反序列化泛型对象)
- [序列化和反序列化集合,集合中保存任意类型的对象](#序列化和反序列化集合集合中保存任意类型的对象)
# gson
## gson简介
gson是一个java库通常用来将java对象转化为其json表示的字符串或者将json格式的字符串转化为其等价的java对象。
## gson使用
在gson中使用最频繁的类是Gson。可以通过new Gson()构造函数来创建Gson对象也可以通过GsonBuilder来创建Gson对象GsonBuidler在创建Gson对象时能够指定一些设置如版本控制等。
由于Gson对象在执行json操作时并不会保存任何状态故而Gson对象是线程安全的单一的Gson对象可以在多线程环境下被重复使用。
### Gson库通过Maven引入
```xml
<dependencies>
<!-- Gson: Java to JSON conversion -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
```
### 基本类型的序列化和反序列化
```java
// Serialization
Gson gson = new Gson();
gson.toJson(1); // ==> 1
gson.toJson("abcd"); // ==> "abcd"
gson.toJson(new Long(10)); // ==> 10
int[] values = { 1 };
gson.toJson(values); // ==> [1]
// Deserialization
int one = gson.fromJson("1", int.class);
Integer one = gson.fromJson("1", Integer.class);
Long one = gson.fromJson("1", Long.class);
Boolean false = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
String[] anotherStr = gson.fromJson("[\"abc\"]", String[].class);
```
### 对象的序列化和反序列化
类似于java自带的序列化机制当成员字段被transient修饰时并不会序列化该字段
```java
class BagOfPrimitives {
private int value1 = 1;
private String value2 = "abc";
private transient int value3 = 3;
BagOfPrimitives() {
// no-args constructor
}
}
// Serialization
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);
// ==> json is {"value1":1,"value2":"abc"}
// Deserialization
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
// ==> obj2 is just like obj
```
### gson和对象联用的使用规范
- gson使用过程中待序列化或反序列化的成员字段可以是private的同时也推荐将待序列化或反序列化的成员字段声明为private
- 没有必要对成员字段使用注解来特定标明在序列化或者反序列化中包含该字段,默认情况下该对象所有成员字段和该对象父类对象所包含的所有字段都会被序列化
- 类似于jdk自带的序列化和反序列化机制如果一个字段被标明为transient该字段将不会被包含到序列化和反序列化过程中
- gson实现能够正确处理字段为空的情况
- 当序列化过程中为null的字段将会被省略
- 当反序列化过程中如果一个字段在json串中并没有被设置反序列化得到的对象中该字段将会被设置为默认值引用类型默认值为null、数字类型默认值为0boolean类型其默认值为false
- 在内部类、匿名类、本地类中关联外部类的字段将会被忽略,并不会包含在序列化和反序列化过程中
### gson和嵌套类的关联使用
gson可以单独的对静态内部类进行序列化和反序列化因为静态内部类并不包含对外部类的引用但是gson无法单独的反序列化内部类因为在反序列化内部类的过程中其无参构造器需要一个指向其外部类对象的引用但是该外部类对象在对内部类进行反序列化时是不可访问的。
可以通过将内部类改为静态的内部类,此时对内部类的反序列化将不会需要指向外部类对象的引用。
### gson和数组的关联使用
gson支持多维数组并且支持任意的复杂元素类型
```java
Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};
// Serialization
gson.toJson(ints); // ==> [1,2,3,4,5]
gson.toJson(strings); // ==> ["abc", "def", "ghi"]
// Deserialization
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class);
// ==> ints2 will be same as ints
```
### gson对java中的集合进行序列化和反序列化
gson可以序列化任意对象的集合但是无法对其进行反序列化因为在反序列化时用户没有任何方法去指定其生成的集合中元素的类型。因而需要通过typeToken来告知Gson需要反序列化的类型。
```java
Gson gson = new Gson();
Collection<Integer> ints = Arrays.asList(1,2,3,4,5);
// Serialization
String json = gson.toJson(ints); // ==> json is [1,2,3,4,5]
// Deserialization
Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();
Collection<Integer> ints2 = gson.fromJson(json, collectionType);
// ==> ints2 is same as ints
```
### gson对Map类型的序列化和反序列化
默认情况下gson会将java中任意的Map实现类型序列化为JSON对象。由于JSON对象其key只支持字符串类型gson会将待序列化的Map key调用toString转化为字符串。如果map中的key为null则序列化后的key为"null"
```java
/**
* gson对map进行序列化
**/
Gson gson = new Gson();
Map<String, String> stringMap = new LinkedHashMap<>();
stringMap.put("key", "value");
stringMap.put(null, "null-entry");
// Serialization
String json = gson.toJson(stringMap); // ==> json is {"key":"value","null":"null-entry"}
Map<Integer, Integer> intMap = new LinkedHashMap<>();
intMap.put(2, 4);
intMap.put(3, 6);
// Serialization
String json = gson.toJson(intMap); // ==> json is {"2":4,"3":6}
```
在反序列化的过程中gson会使用为Map key类型注册的TypeAdapter的read方法来进行反序列化。为了让gson知道反序列化得到的Map对象的key和value类型需要使用TypeToken。
```java
Gson gson = new Gson();
Type mapType = new TypeToken<Map<String, String>>(){}.getType();
String json = "{\"key\": \"value\"}";
// Deserialization
Map<String, String> stringMap = gson.fromJson(json, mapType);
// ==> stringMap is {key=value}
```
默认情况下gson序列化map时复杂类型的key会调用toString方法来将其转化成字符串。但是gson同样支持开启复杂类型key的序列化操作。通过Gson.enableComplexMapKeySerialization()方法来开启Gson会调用为Map的key类型注册的TypeAdapter的write方法来序列化key而不是通过toString方法将key转化为字符串。
> 当Map中任意一条Entry的key通过Adapter被序列化为了JSON数组或者对象那么Gson会将整个Map序列化为Json数组数组元素为map中entry的键值对。如果map中所有的entry key都不会被序列化为json object或json array那么该map将会被序列化为json对象
> 在对枚举型key进行反序列化的过程中如果enum找不到一个具有匹配name()值的常量时其会采用一个回退机制根据枚举常量的toString()值来进行反序列化的匹配。
### 序列化和反序列化泛型对象
当gson对object对象调用toJson方法时gson会调用object.getClass()来获取需要序列化的字段信息。类似的在gson调用fromJson时会向fromJson方法传递一个MyClass.class对象。该方法在序列化和反序列化类型是非泛型类型时能够正常运行。
但是,当待序列化和反序列化的类型是泛型类型时,在序列化和反序列化对象时泛型类型信息会丢失,因为泛型采用的是类型擦除。
```java
class Foo<T> {
T value;
}
Gson gson = new Gson();
Foo<Bar> foo = new Foo<Bar>();
gson.toJson(foo); // May not serialize foo.value correctly
gson.fromJson(json, foo.getClass()); // Fails to deserialize foo.value as Bar
```
> 上述代码中foo.getClass()方法返回的只是Foo.class对象并不会包含泛型类型Bar的信息故而在反序列化时gson并不知道应该将value反序列化为Bar类型
可以通过向fromJson中传入Type参数来详细指定想要将json串转化成的泛型类型信息
```java
Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);
gson.fromJson(json, fooType);
```
### 序列化和反序列化集合,集合中保存任意类型的对象
当JSON串中数组包含各种类型元素时将其转化为包含任意类型的java集合可以有如下方法
- 使用Gson Parser APIJsonParser底层parser api将json串中数组转化为JsonArray并且为每个元素调用Gson.fromJson。该方法是推荐的方法
> gson.fromJson可以针对String、Reader、JsonElement来调用