您的位置:首页 > 编程语言 > Java开发

Java json序列化库gson(2)

2017-07-05 00:00 351 查看
基于策略(自定义规则)

上面介绍的了3种排除字段的方法,说实话我除了@Expose以外,其它的都是只在Demo用上过,用得最多的就是马上要介绍的自定义规则,好处是功能强大、灵活,缺点是相比其它3种方法稍麻烦一点,但也仅仅只是想对其它3种稍麻烦一点而已。

基于策略是利用Gson提供的
ExclusionStrategy
接口,同样需要使用
GsonBuilder
,相关API 2个,分别是
addSerializationExclusionStrategy
addDeserializationExclusionStrategy
分别针对序列化和反序化时。这里以序列化为例。

Gson gson = new GsonBuilder()
.addSerializationExclusionStrategy(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
// 这里作判断,决定要不要排除该字段,return true为排除
if ("finalField".equals(f.getName())) return true; //按字段名排除
Expose expose = f.getAnnotation(Expose.class);
if (expose != null && expose.deserialize() == false) return true; //按注解排除
return false;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
// 直接排除某个类 ,return true为排除
return (clazz == int.class || clazz == Integer.class);
}
})
.create();


二、 POJO与JSON的字段映射规则

还是之前User的例子,已经去除所有注解:

User user = new User("怪盗kidou", 24);
user.emailAddress = "ikidou@example.com";

GsonBuilder
提供了
FieldNamingStrategy
接口和
setFieldNamingPolicy
setFieldNamingStrategy
两个方法。

默认实现
GsonBuilder.setFieldNamingPolicy
方法与Gson提供的另一个枚举类
FieldNamingPolicy
配合使用,该枚举类提供了5种实现方式分别为:

FieldNamingPolicy结果(仅输出emailAddress字段)
IDENTITY{"emailAddress":"ikidou@example.com"}
LOWER_CASE_WITH_DASHES{"email-address":"ikidou@example.com"}
LOWER_CASE_WITH_UNDERSCORES{"email_address":"ikidou@example.com"}
UPPER_CAMEL_CASE{"EmailAddress":"ikidou@example.com"}
UPPER_CAMEL_CASE_WITH_SPACES{"Email Address":"ikidou@example.com"}
自定义实现
GsonBuilder.setFieldNamingStrategy
方法需要与Gson提供的
FieldNamingStrategy
接口配合使用,用于实现将POJO的字段与JSON的字段相对应。上面的
FieldNamingPolicy
实际上也实现了
FieldNamingStrategy
接口,也就是说
FieldNamingPolicy
也可以使用
setFieldNamingStrategy
方法。

用法:

Gson gson = new GsonBuilder()
.setFieldNamingStrategy(new FieldNamingStrategy() {
@Override
public String translateName(Field f) {
//实现自己的规则
return null;
}
})
.create();

注意:
@SerializedName
注解拥有最高优先级,在加有
@SerializedName
注解的字段上
FieldNamingStrategy
不生效!

一、TypeAdapter

TypeAdapter
是Gson自2.0(源码注释上说的是2.1)开始版本提供的一个抽象类,用于接管某种类型的序列化和反序列化过程,包含两个注要方法
write(JsonWriter,T)
read(JsonReader)
其它的方法都是
final
方法并最终调用这两个抽象方法。

public abstract class TypeAdapter<T> {
public abstract void write(JsonWriter out, T value) throws IOException;
public abstract T read(JsonReader in) throws IOException;
//其它final 方法就不贴出来了,包括`toJson`、`toJsonTree`、`toJson`和`nullSafe`方法。
}

注意:TypeAdapter 以及 JsonSerializer 和 JsonDeserializer 都需要与
GsonBuilder.registerTypeAdapter
示或
GsonBuilder.registerTypeHierarchyAdapter
配合使用,下面将不再重复说明。实例如下:

User user = new User("怪盗kidou", 24);
user.emailAddress = "ikidou@example.com";
Gson gson = new GsonBuilder()
//为User注册TypeAdapter
.registerTypeAdapter(User.class, new UserTypeAdapter())
.create();
System.out.println(gson.toJson(user));

UserTypeAdapter的定义:

public class UserTypeAdapter extends TypeAdapter<User> {

@Override
public void write(JsonWriter out, User value) throws IOException {
out.beginObject();
out.name("name").value(value.name);
out.name("age").value(value.age);
out.name("email").value(value.email);
out.endObject();
}

@Override
public User read(JsonReader in) throws IOException {
User user = new User();
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "name":
user.name = in.nextString();
break;
case "age":
user.age = in.nextInt();
break;
case "email":
case "email_address":
case "emailAddress":
user.email = in.nextString();
break;
}
}
in.endObject();
return user;
}
}

当我们为
User.class
注册了
TypeAdapter
之后,只要是操作
User.class
那些之前介绍的
@SerializedName
FieldNamingStrategy
Since
Until
Expos
通通都黯然失色,失去了效果,只会调用我们实现的
UserTypeAdapter.write(JsonWriter, User)
方法,我想怎么写就怎么写。

再说一个场景,在该系列的第一篇文章就说到了Gson有一定的容错机制,比如将字符串
"24"
转成int 的
24
,但如果有些情况下给你返了个空字符串怎么办(有人给我评论问到这个问题)?虽然这是服务器端的问题,但这里我们只是做一个示范。

int型会出错是吧,根据我们上面介绍的,我注册一个TypeAdapter 把 序列化和反序列化的过程接管不就行了?

Gson gson = new GsonBuilder()
.registerTypeAdapter(Integer.class, new TypeAdapter<Integer>() {
@Override
public void write(JsonWriter out, Integer value) throws IOException {
out.value(String.valueOf(value));
}
@Override
public Integer read(JsonReader in) throws IOException {
try {
return Integer.parseInt(in.nextString());
} catch (NumberFormatException e) {
return -1;
}
}
})
.create();
System.out.println(gson.toJson(100)); // 结果:"100"
System.out.println(gson.fromJson("\"\"",Integer.class)); // 结果:-1

注:测试空串的时候一定是
"\"\""
而不是
""
""
代表的是没有json串,
"\"\""
才代表json里的
""


你说这一接管就要管两样好麻烦呀,我明明只想管序列化(或反列化)的过程的,另一个过程我并不关心,难道没有其它更简单的方法么? 当然有!就是接下来要介绍的 JsonSerializer与JsonDeserializer

二、JsonSerializer与JsonDeserializer

JsonSerializer
JsonDeserializer
不用像
TypeAdapter
一样,必须要实现序列化和反序列化的过程,你可以据需要选择,如只接管序列化的过程就用
JsonSerializer
,只接管反序列化的过程就用
JsonDeserializer
,如上面的需求可以用下面的代码。

Gson gson = new GsonBuilder()
.registerTypeAdapter(Integer.class, new JsonDeserializer<Integer>() {
@Override
public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
return json.getAsInt();
} catch (NumberFormatException e) {
return -1;
}
}
})
.create();
System.out.println(gson.toJson(100)); //结果:100
System.out.println(gson.fromJson("\"\"", Integer.class)); //结果-1

下面是所有数字都转成序列化为字符串的例子

JsonSerializer<Number> numberJsonSerializer = new JsonSerializer<Number>() {
@Override
public JsonElement serialize(Number src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(String.valueOf(src));
}
};
Gson gson = new GsonBuilder()
.registerTypeAdapter(Integer.class, numberJsonSerializer)
.registerTypeAdapter(Long.class, numberJsonSerializer)
.registerTypeAdapter(Float.class, numberJsonSerializer)
.registerTypeAdapter(Double.class, numberJsonSerializer)
.create();
System.out.println(gson.toJson(100.0f));//结果:"100.0"


registerTypeAdapter与registerTypeHierarchyAdapter的区别:

registerTypeAdapterregisterTypeHierarchyAdapter
支持泛型
支持继承
注:如果一个被序列化的对象本身就带有泛型,且注册了相应的
TypeAdapter
,那么必须调用
Gson.toJson(Object,Type)
,明确告诉Gson对象的类型。

Type type = new TypeToken<List<User>>() {}.getType();
TypeAdapter typeAdapter = new TypeAdapter<List<User>>() {
//略
};
Gson gson = new GsonBuilder()
.registerTypeAdapter(type, typeAdapter)
.create();
List<User> list = new ArrayList<>();
list.add(new User("a",11));
list.add(new User("b",22));
//注意,多了个type参数
String result = gson.toJson(list, type);


三、TypeAdapterFactory

TypeAdapterFactory,见名知意,用于创建TypeAdapter的工厂类,通过对比
Type
,确定有没有对应的
TypeAdapter
,没有就返回null,与
GsonBuilder.registerTypeAdapterFactory
配合使用。

Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new TypeAdapterFactory() {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
return null;
}
})
.create();


四、@JsonAdapter注解

JsonAdapter
相较之前介绍的
SerializedName
FieldNamingStrategy
Since
Until
Expos
这几个注解都是比较特殊的,其它的几个都是用在POJO的字段上,而这一个是用在POJO类上的,接收一个参数,且必须是
TypeAdpater
JsonSerializer
JsonDeserializer
这三个其中之一。

上面说
JsonSerializer
JsonDeserializer
都要配合
GsonBuilder.registerTypeAdapter
使用,但每次使用都要注册也太麻烦了,
JsonAdapter
就是为了解决这个痛点的。

使用方法(以User为例):

@JsonAdapter(UserTypeAdapter.class) //加在类上
public class User {
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public User(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
public String name;
public int age;
@SerializedName(value = "emailAddress")
public String email;
}

使用时不用再使用
GsonBuilder
去注册
UserTypeAdapter
了。
注:
@JsonAdapter
仅支持
TypeAdapter
TypeAdapterFactory


Gson gson = new Gson();
User user = new User("怪盗kidou", 24, "ikidou@example.com");
System.out.println(gson.toJson(user));
//结果:{"name":"怪盗kidou","age":24,"email":"ikidou@example.com"}
//为区别结果,特意把email字段与@SerializedName注解中设置的不一样


五、TypeAdapter与 JsonSerializer、JsonDeserializer对比

TypeAdapterJsonSerializer、JsonDeserializer
引入版本2.01.x
Stream API支持不支持*,需要提前生成
JsonElement
内存占用
TypeAdapter
效率
TypeAdapter
作用范围序列化 反序列化序列化 反序列化
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  JSON Gson