[TOC]
一位博主翻译的系列教程,都是官方推荐教程。
Gson教程七(译):@SerializedName和@Expose
Gson教程八(译):GsonBuilder基础以及命名策略
Gson教程十(译):Exclusion Strategies
Gson教程十二(译):Float和Double类型的特殊值
一、Gson 介绍
Gson 是一个 Java 对象与 Json 格式互转的类库。
- 提供简单的 toJson() 和 fromJson() 方法用于 Java 对象和 Json 互转
- 允许已存在的、无法修改的类与 Json 互转
- 支持泛型
- 允许自定义对象的Json格式
- 支持复杂对象(多层继承、泛型)
Gradle:
1
2
3
dependencies {
implementation 'com.google.code.gson:gson:2.8.5'
}
Maven:
1
2
3
4
5
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
二、基本使用
1. 序列化
- 字符串类型 String 转为 “xxx”:”name”: “Norman”
- 非字符串类型 int、boolean 等转为 xxx:”age”: 26
- 对象转为 {xxx}:”userAddress”: {xxx}
- 数组转为 [xxx]:”families”: [xxx]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@AllArgsConstructor
class UserNested {
String name;
String email;
int age;
boolean isDeveloper;
UserAddress userAddress;
String[] families;
}
@AllArgsConstructor
class UserAddress {
String street;
String houseNumber;
String city;
String country;
}
UserAddress userAddress = new UserAddress(
"Main Street",
"42A",
"Magdeburg",
"Germany"
);
UserNested userObject = new UserNested(
"Norman",
"norman@futurestud.io",
26,
true,
userAddress,
new String[]{"mother", "father", "son"}
);
Gson gson = new Gson();
String userWithAddressJson = gson.toJson(userObject);
System.out.println(userWithAddressJson);
输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"name": "Norman",
"email": "norman@futurestud.io",
"age": 26,
"isDeveloper": true,
"userAddress": {
"street": "Main Street",
"houseNumber": "42A",
"city": "Magdeburg",
"country": "Germany"
},
"families": [
"mother",
"father",
"son"
]
}
2. 反序列化
反序列化时需要指定具体类:gson.fromJson(restaurantJson, Restaurant.class)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Data
@AllArgsConstructor
class Restaurant {
String name;
Owner owner;
Cook cook;
Waiter waiter;
}
@Data
@AllArgsConstructor
class Owner {
String name;
UserAddress address;
}
@Data
@AllArgsConstructor
class Cook {
String name;
int age;
int salary;
}
@Data
@AllArgsConstructor
class Waiter {
String name;
int age;
int salary;
}
String restaurantJson = "{ 'name':'Future Studio Steak House', 'owner':{ 'name':'Christian', 'address':{ 'city':'Magdeburg', 'country':'Germany', 'houseNumber':'42', 'street':'Main Street'}},'cook':{ 'age':18, 'name': 'Marcus', 'salary': 1500 }, 'waiter':{ 'age':18, 'name': 'Norman', 'salary': 1000}}";
gson = new Gson();
Restaurant restaurantObject = gson.fromJson(restaurantJson, Restaurant.class);
System.out.println(restaurantObject);
输出
1
Restaurant(name=Future Studio Steak House, owner=Owner(name=Christian, address=UserAddress(street=Main Street, houseNumber=42, city=Magdeburg, country=Germany)), cook=Cook(name=Marcus, age=18, salary=1500), waiter=Waiter(name=Norman, age=18, salary=1000))
3. 空值映射
- While serializing, a null field is omitted from the output.
- While deserializing, a missing entry in JSON results in setting the corresponding field in the object to its default value: null for object types, zero for numeric types, and false for booleans.
默认情况下
- 序列化时,Java对象里的空值被忽略
- 反序列化时,Json里的缺失项会被赋予默认值:对象为 null,数值类型为 0,布尔类型为 false
1
2
3
4
5
6
7
8
9
10
11
12
13
@Data
@AllArgsConstructor
class UserSimple {
String name;
String email;
int age;
boolean isDeveloper;
}
UserSimple userObject = new UserSimple(null, "norman@futurestud.io", 26, true);
Gson gson = new Gson();
String userJson = gson.toJson(userObject);
System.out.println(userJson);
输出
1
2
3
4
5
{
"email": "norman@futurestud.io",
"age": 26,
"isDeveloper": true
}
三、泛型
对于泛型会遇到泛型擦除的问题。譬如,对于 **Result
1
2
3
4
5
public class Result<T> {
public int code;
public String message;
public T data;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
UserAddress userAddress = new UserAddress(
"Main Street",
"42A",
"Magdeburg",
"Germany"
);
Result<UserAddress> result = new Result<>(200, "success", userAddress);
Gson gson = new Gson();
String resultJson = gson.toJson(result);
System.out.println(resultJson);
// Result.class方式下,T的类型为LinkedTreeMap
result = gson.fromJson(resultJson, Result.class);
System.out.println(result);
// TypeToken方式下,T的类型为UserAddress
result = gson.fromJson(resultJson, new TypeToken<Result<UserAddress>>(){}.getType());
System.out.println(result);
Result.class,data 类型为:LinkedTreeMap。
TypeToken,data 类型为:UserAddress。
四、其他使用
1. GsonBuilder
new Gson() 只能获取默认配置的 Gson,通过 GsonBuilder 可以实现多种配置。几个常用配置:
serializeNulls(),对 null 值进行序列化。
1 2 3 4 5 6
GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.serializeNulls(); Gson gson = gsonBuilder.create(); UserSimple user = new UserSimple("Norman", null, 26, true); String usersJson = gson.toJson(user);
输出,email 的 null 也进行了序列化。
1 2 3 4 5 6
{ "age": 26, "email": null, "isDeveloper": true, "name": "Norman" }
setFieldNamingPolicy(FieldNamingPolicy namingConvention),Java 对象转为 Json 时所有域名的命名规则。
- FieldNamingPolicy.IDENTITY:保持域名不变
- FieldNamingPolicy.UPPER_CAMEL_CASE:大驼峰,someFieldName —> SomeFieldName
- FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES:大驼峰+空格,someFieldName —> Some Field Name
- FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES:小写+下划线,someFieldName —> some_field_name
- FieldNamingPolicy.LOWER_CASE_WITH_DASHES:小写+中划线,someFieldName —> some-field-name
- FieldNamingPolicy.LOWER_CASE_WITH_DOTS:小写+点,someFieldName —> some.field.name
setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy),FieldNamingPolicy 无法满足使用要求时,可以自定义域名的命名策略。
1 2 3 4 5 6 7 8
UserSimple userObject = new UserSimple(null, "norman@futurestud.io", 26, true); GsonBuilder gb = new GsonBuilder(); gb.setFieldNamingStrategy(f -> f.getName().toUpperCase()); Gson gson = gb.create(); String userJson = gson.toJson(userObject); System.out.println(userJson);
输出,所有域名都是大写了。
1 2 3 4 5
{ "EMAIL": "norman@futurestud.io", "AGE": 26, "ISDEVELOPER": true }
setExclusionStrategies(ExclusionStrategy… strategies),自定义的域排除策略,有两个方法供重写,重写后返回为 true 的域将被排除在外,不进行序列化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
UserSimple userObject = new UserSimple("Tony", "norman@futurestud.io", 26, true); GsonBuilder gb = new GsonBuilder(); gb.setExclusionStrategies(new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes f) { return f.getName().startsWith("e"); } @Override public boolean shouldSkipClass(Class<?> clazz) { return clazz == int.class; } }); Gson gson = gb.create(); String userJson = gson.toJson(userObject); System.out.println(userJson); gson = new Gson(); userJson = gson.toJson(userObject); System.out.println(userJson);
输出,开头为 e 的域被排除、整数型的域被排除。
1 2 3 4
{ "name": "Tony", "isDeveloper": true }
另外还有两个可以单独配置序列化、反序列化的方法:
- addSerializationExclusionStrategy(ExclusionStrategy strategy)
- addDeserializationExclusionStrategy(ExclusionStrategy strategy)
setPrettyPrinting(),格式化输出,默认的输出格式都挤在一行,没有换行和缩进。
1 2 3 4 5 6 7 8 9
// 格式化后的 { "name": "Tony", "email": "norman@futurestud.io", "age": 26, "isDeveloper": true } // 格式化前的 {"name":"Tony","email":"norman@futurestud.io","age":26,"isDeveloper":true}
setVersion(double ignoreVersionsAfter),排除设定版本外的域,结合 @Since、@Until 域注解使用,详见后文。
excludeFieldsWithoutExposeAnnotation(),排除没有 expose 注解的域,没有 @Expose 注解的域都不会被序列化和反序列化,详见后文。
2. 域注解
@Since,指定该域在某一版本及之后才存在。Java 对象转 Json 以 setVersion(double ignoreVersionsAfter) 中设定的版本为界,如 setVersion(0.99) 设定了版本为 0.99,那么所有 @Since 大于 0.99 的域都不会转 Json。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
@Data @AllArgsConstructor class UserSimple { String name; String email; int age; @Since(1.0) boolean isDeveloper; } UserSimple userObject = new UserSimple("Tony", "norman@futurestud.io", 26, true); GsonBuilder gb = new GsonBuilder(); gb.setPrettyPrinting(); gb.setVersion(0.99); Gson gson = gb.create(); String userJson = gson.toJson(userObject); System.out.println(userJson);
输出,版本 1.0 > 版本 0.99,所以 isDeveloper 被排除。
1 2 3 4 5
{ "name": "Tony", "email": "norman@futurestud.io", "age": 26 }
@Until,指定该域一直存在到某一版本。如 setVersion(1.01) 设定了版本为 1.01,那么所有 @Until 小于 1.01 的域都不会转 Json。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
@Data @AllArgsConstructor class UserSimple { String name; String email; int age; @Until(1.0) boolean isDeveloper; } UserSimple userObject = new UserSimple("Tony", "norman@futurestud.io", 26, true); GsonBuilder gb = new GsonBuilder(); gb.setPrettyPrinting(); gb.setVersion(1.01); Gson gson = gb.create(); String userJson = gson.toJson(userObject); System.out.println(userJson);
输出,版本 1.0 < 版本 1.01,所以 isDeveloper 被排除。
1 2 3 4 5
{ "name": "Tony", "email": "norman@futurestud.io", "age": 26 }
@Expose,配置 excludeFieldsWithoutExposeAnnotation() 后,没有 @Expose 注解的域将不会转为 Json。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
@Data @AllArgsConstructor class UserSimple { @Expose String name; @Expose String email; @Expose int age; boolean isDeveloper; } UserSimple userObject = new UserSimple("Tony", "norman@futurestud.io", 26, true); GsonBuilder gb = new GsonBuilder(); gb.setPrettyPrinting(); gb.excludeFieldsWithoutExposeAnnotation(); Gson gson = gb.create(); String userJson = gson.toJson(userObject); System.out.println(userJson);
输出,没有 @Expose 注解的 isDeveloper 被排除了。
1 2 3 4 5
{ "name": "Tony", "email": "norman@futurestud.io", "age": 26 }
transient、static,这两个关键字修饰的域默认不转 Json。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
@Data @AllArgsConstructor class UserSimple { String name; String email; static int age; transient boolean isDeveloper; } UserSimple userObject = new UserSimple("Tony", "norman@futurestud.io", true); GsonBuilder gb = new GsonBuilder(); gb.setPrettyPrinting(); Gson gson = gb.create(); String userJson = gson.toJson(userObject);
输出,static 修饰的 age 和 transient 修饰的 isDeveloper 都被排除。
1 2 3 4
{ "name": "Tony", "email": "norman@futurestud.io" }
@SerializedName,自定序列化和反序列化时 Json 中对应的域名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
@Data @AllArgsConstructor class UserSimple { @SerializedName("MyName") String name; String email; @SerializedName("YourAge") int age; boolean isDeveloper; } UserSimple userObject = new UserSimple("Tony", "norman@futurestud.io", 26, true); GsonBuilder gb = new GsonBuilder(); gb.setPrettyPrinting(); Gson gson = gb.create(); String userJson = gson.toJson(userObject); System.out.println(userJson);
输出,name 对应 MyName,age 对应 YourAge。
1 2 3 4 5 6
{ "MyName": "Tony", "email": "norman@futurestud.io", "YourAge": 26, "isDeveloper": true }