在详细介绍Gson的序列化和反序列化之前,先通过自己在开发过程中遇到的一个问题以及简单的解决方案来逐步引入分析它的工作方式。当然该解决方案不具有通用性,这个在博客最后说明也有一个相对而言比较高效的解决方式,文章后部分会有相关说明:
在我的一个项目开中出现了如下错误导致程序加载不了数据:
Expected BEGIN_ARRAY but was STRING at line 1 column 27
经过排查原来是一个字段需要是数组类型,但是经过后台编辑人员写入数据库的是一个空的字符串,实际上应该传null。其实解决这个问题也很简单,让提供接口的人员对该字段进行非空判断,如果是“”,返回null就可以解决。其实还可以用另一种Gson自带的解决方法:
测试json串:
{
'name': 'java',
'authors': [{'id':1','name':Joshua Bloch'},{'id':2','name':'Tom'}]
}
为此我定义了两个Java类:
/**
*书本类
**/
public class Book {
private Author[] authors;
private String name;
public Author[] getAuthors() {
return authors;
}
public void setAuthors(Author[] authors) {
this.authors = authors;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/***
*书本作者类
**/
public class Author {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
如果正常情况下是可以解析完成的,但是在传的时候传了如下json,是肯定会出错的:
{
'name': 'java',
'authors': ''
}
所以针对这种情况,用Gson的JsonDeserializer可以很好地处理这种情况:
public class BookDeserializer implements JsonDeserializer{
@Override
public Book deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
final JsonObject jsonObject = json.getAsJsonObject();
final JsonElement jsonTitle = jsonObject.get("name");
final String name = jsonTitle.getAsString();
JsonElement jsonAuthors= jsonObject.get("authors");
final Book book = new Book();
if(jsonAuthors.isJsonArray()) {//如果数组类型,此种情况是我们需要的
//关于context在文章最后有简单说明
Author[] authors = context.deserialize(jsonAuthors, Author[].class);
book.setAuthors(authors);
}else {//此种情况为无效情况
book.setAuthors(null);
}
book.setName(name);
return book;
}
public static void main(String[] args) throws Exception {
// Configure Gson
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Book.class, new BookDeserializer());
gsonBuilder.registerTypeAdapter(Author.class, new AuthorDeserializer());
Gson gson = gsonBuilder.create();
String str ="{ \"name\":\"java书籍\", \"authors\":[{\"id\":5,\"name\":\"Tome\"}, {\"id\":6,\"name\":\"Jerry\"}]}";
// String str ="{ \"name\":\"java书籍\", \"authors\":\"\"}";
Book book = gson.fromJson(str, Book.class);
String json = gson.toJson(book);
System.out.println(json);
}
}
public class AuthorDeserializer implements JsonDeserializer {
@Override
public Object deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
final JsonObject jsonObject = json.getAsJsonObject();
final Author author = new Author();
author.setId(jsonObject.get("id").getAsInt());
author.setName(jsonObject.get("name").getAsString());
return author;
}
}
代码很简单,就是如果是isJsonArry就把信息传递进去,如果是“”等无效信息直接传null.
使用JsonDeserializer代码很简单:
GsonBuilder gsonBuilder = new GsonBuilder();
//注册TypeAdapter
gsonBuilder.registerTypeAdapter(Book.class, new BookDeserializer());
gsonBuilder.registerTypeAdapter(Author.class, new AuthorDeserializer());
Gson gson = gsonBuilder.create();
GsonBuilder是建造者模式的简单应用,主要是通过注册自定义的一些TypeAdapter或者JsonDeserializer来创建一个Gson对象。通过《TypeAdapter的工作原理》我们知道,通过fromJson来解析的时候最终会先把TypeAdapter封装成TypeAdapterFctory,最终在解析的时候通过getAdapter来获取Factory中的typeadapter进行read操作来完成json的解析。结合《TypeAdapter的工作原理》这篇博客,所以有理由猜想一下JsonDesrializer的工作方式应该是如下的方式:
1)将JsonDesrializer封装成TypeAdapterFactory
2)在Factory的create方法 返回TypeAdapter的时候,需要再次将JsonDesrializer封装成TypeAdapter
3)然后在Gson的getTypeAdapter 方法,返回封装过后的TypeAdapter,并调用read方法
4)JsonDesrializer在TypeAdapter起到代理的作用,在reader方法执行的时候实际上调用的是JsonDesrializer的deserializer方法
关于上面的猜想是否正确,我在读源码实现之前也是抱着这个猜想来追踪代码的,结果证明了我猜想的正确性!下面就看看Gson源码中是怎么处理JsonDesrializer的。
1)通过registerTypeAdapter将JsonDesrializer封装成SingleTypeFactory 对象
public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
//此处省略部分代码
//在我们的程序中执行了这么一段代码
if (typeAdapter instanceof JsonSerializer || typeAdapter instanceof JsonDeserializer) {
TypeToken typeToken = TypeToken.get(type);
//通过TreeTypeAdapter的newFactoryWithMatchRawTye方法将JsonSerializer封装成TypeAdapterFactory
factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter));
}
//此处有省略代码
return this;
}
public static TypeAdapterFactory newFactoryWithMatchRawType(
TypeToken exactType, Object typeAdapter) {
// only bother matching raw types if exact type is a raw type
boolean matchRawType = exactType.getType() == exactType.getRawType();
//将JsonSerializer封装成SingleTypeFactory并返回
return new SingleTypeFactory(typeAdapter, exactType, matchRawType, null);
}
private static class SingleTypeFactory implements TypeAdapterFactory {
private final TypeToken exactType;
private final boolean matchRawType;
private final Class hierarchyType;
private final JsonSerializer serializer;
//自定义的deserializer,对应的是typeAdapter参数
private final JsonDeserializer deserializer;
private SingleTypeFactory(Object typeAdapter, TypeToken exactType, boolean matchRawType,
Class hierarchyType) {
serializer = typeAdapter instanceof JsonSerializer
? (JsonSerializer) typeAdapter
: null;
deserializer = typeAdapter instanceof JsonDeserializer
? (JsonDeserializer) typeAdapter
: null;
$Gson$Preconditions.checkArgument(serializer != null || deserializer != null);
this.exactType = exactType;
this.matchRawType = matchRawType;
this.hierarchyType = hierarchyType;
}
/*****
* TypeAdapterFactory的create方法,正如上面的猜想:
* 将deserializer重新封装成TreeTypeAdapter方法对象并返回之
***/
@SuppressWarnings("unchecked") // guarded by typeToken.equals() call
public TypeAdapter create(Gson gson, TypeToken type) {
boolean matches = exactType != null
? exactType.equals(type) || matchRawType && exactType.getType() == type.getRawType()
: hierarchyType.isAssignableFrom(type.getRawType());
return matches
? new TreeTypeAdapter((JsonSerializer) serializer,
(JsonDeserializer) deserializer, gson, type, this)
: null;
}
}
通过上面的代码可以发现:通过SingleTypeFactory 的create方法,将deserializer交给TreeTypeAdapter,并返回一个TreeTypeAdapter对象.最终在解析的时候会调用Gson的getTypeAdapter().read来进行操作。那么就看看TreeTypeAdapter的read方法都做了什么:
private TreeTypeAdapter(JsonSerializer serializer, JsonDeserializer deserializer,
Gson gson, TypeToken typeToken, TypeAdapterFactory skipPast) {
this.serializer = serializer;
this.deserializer = deserializer;
this.gson = gson;
this.typeToken = typeToken;
this.skipPast = skipPast;
}
@Override public T read(JsonReader in) throws IOException {
if (deserializer == null) {
return delegate().read(in);
}
//通过Streams.parse(in)方法现将json串解析成JsonElement对象,然后把这个对象交给deserialize处理之
JsonElement value = Streams.parse(in);
if (value.isJsonNull()) {
return null;
}
//正式这儿,json的解析操作交给了deserializer 处理!!!
return deserializer.deserialize(value, typeToken.getType(), gson.deserializationContext);
}
看到了吧,json的解析操作交给了deserializer 进行处理!read方法先通过Streams.parse方法将json串解析成JsonElement对象(详细解析实现分析
见此处),然后在将JsonElement对象转换成你自定义的Java对象。JsonDeserializer与纯粹的TypeAdapter相比,多了转换成JsonElment对象的操作。所以效率上没有直接使用TypeAdpter高,因为TypeAdapter直接将java串转换成了Java Object!
用图来表示JsonDeserializer的工作原理如下:
而TypeAdapter的工作方式是:
很直观的就可以看出来还是TypeAdapter的工作效率相对比较高!(图片来源):所以文章开头的解决问题的方式可以用TypeAdpter也可以简单处理之!文章的最后再简单的说一下JsonDeserializer的JsonDeserializationContext问题,在上面的read方法中,deserialize方法的最后一个参数传的是gson.deserializationContext,在Gson里面是其实现方案:
final JsonDeserializationContext deserializationContext = new JsonDeserializationContext() {
@SuppressWarnings("unchecked")
public T deserialize(JsonElement json, Type typeOfT) throws JsonParseException {
//调用gson的fromJson方法进行解析
return (T) fromJson(json, typeOfT);
}
};