1、Gson使用指南4注:此系列基于Gson 2.4。本次文章的主要内容: TypeAdapter JsonSerializer与JsonDeserializer TypeAdapterFactory JsonAdapter注解 TypeAdapter与 JsonSerializer、JsonDeserializer对比 TypeAdapter实例 结语 后期预告一、TypeAdapterTypeAdapter是Gson自2.0(源码注释上说的是2.1)开始版本提供的一个抽象类,用于接管某种类型的序列化和反序列化过程,包含两个注要方法write(JsonWriter,T)和read(JsonRea
2、der)其它的方法都是final方法并最终调用这两个抽象方法。12345public abstract class TypeAdapter 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 和 JsonDeseria
3、lizer 都需要与GsonBuilder.registerTypeAdapter示或GsonBuilder.registerTypeHierarchyAdapter配合使用,下面将不再重复说明。使用示例:1234567User user = new User(怪盗kidou, 24);user.emailAddress = ikidou;Gson gson = new GsonBuilder()/为User注册TypeAdapter.registerTypeAdapter(User.class, new UserTypeAdapter().create();System.out.printl
4、n(gson.toJson(user);UserTypeAdapter的定义:12345678910111213141516171819202122232425262728293031323334public class UserTypeAdapter extends TypeAdapter Overridepublic void write(JsonWriter out, User value) throws IOException out.beginObject();out.name(name).value(value.name);out.name(age).value(value.age
5、);out.name(email).value(value.email);out.endObject();Overridepublic 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_a
6、ddress: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有一定的容错机制
7、,比如将字符串24转成int 的24,但如果有些情况下给你返了个空字符串怎么办(有人给我评论问到这个问题)?虽然这是服务器端的问题,但这里我们只是做一个示范。int型会出错是吧,根据我们上面介绍的,我注册一个TypeAdapter 把 序列化和反序列化的过程接管不就行了?123456789101112131415161718Gson gson = new GsonBuilder().registerTypeAdapter(Integer.class, new TypeAdapter() Overridepublic void write(JsonWriter out, Integer valu
8、e) throws IOException out.value(String.valueOf(value); Overridepublic Integer read(JsonReader in) throws IOException try return Integer.parseInt(in.nextString(); catch (NumberFormatException e) return -1;).create();System.out.println(gson.toJson(100); / 结果:100System.out.println(gson.fromJson(,Intege
9、r.class); / 结果:-1注:测试空串的时候一定是而不是,代表的是没有json串,才代表json里的。你说这一接管就要管两样好麻烦呀,我明明只想管序列化(或反列化)的过程的,另一个过程我并不关心,难道没有其它更简单的方法么? 当然有!就是接下来要介绍的JsonSerializer与JsonDeserializer。二、JsonSerializer与JsonDeserializerJsonSerializer和JsonDeserializer不用像TypeAdapter一样,必须要实现序列化和反序列化的过程,你可以据需要选择,如只接管序列化的过程就用JsonSerializer,只接管反
10、序列化的过程就用JsonDeserializer,如上面的需求可以用下面的代码。1234567891011121314Gson gson = new GsonBuilder().registerTypeAdapter(Integer.class, new JsonDeserializer() Overridepublic Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException try return json.getAsInt
11、(); catch (NumberFormatException e) return -1;).create();System.out.println(gson.toJson(100); /结果:100System.out.println(gson.fromJson(, Integer.class); /结果-1下面是所有数字都转成序列化为字符串的例子12345678910111213JsonSerializer numberJsonSerializer = new JsonSerializer() Overridepublic JsonElement serialize(Number src
12、, 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).registe
13、rTypeAdapter(Double.class, numberJsonSerializer).create();System.out.println(gson.toJson(100.0f);/结果:100.0注:registerTypeAdapter必须使用包装类型,所以int.class,long.class,float.class和double.class是行不通的。同时不能使用父类来替上面的子类型,这也是为什么要分别注册而不直接使用Number.class的原因。上面特别说明了registerTypeAdapter不行,那就是有其它方法可行咯?当然!换成registerTypeHie
14、rarchyAdapter就可以使用Number.class而不用一个一个的当独注册啦!registerTypeAdapter与registerTypeHierarchyAdapter的区别:registerTypeAdapterregisterTypeHierarchyAdapter支持泛型是否支持继承否是注:如果一个被序列化的对象本身就带有泛型,且注册了相应的TypeAdapter,那么必须调用Gson.toJson(Object,Type),明确告诉Gson对象的类型。123456789101112Type type = new TypeTokenList() .getType();Ty
15、peAdapter typeAdapter = new TypeAdapterList() /略;Gson gson = new GsonBuilder().registerTypeAdapter(type, typeAdapter).create();List list = new ArrayList();list.add(new User(a,11);list.add(new User(b,22);/注意,多了个type参数String result = gson.toJson(list, type);三、TypeAdapterFactoryTypeAdapterFactory,见名知意,
16、用于创建TypeAdapter的工厂类,通过对比Type,确定有没有对应的TypeAdapter,没有就返回null,与GsonBuilder.registerTypeAdapterFactory配合使用。12345678Gson gson = new GsonBuilder().registerTypeAdapterFactory(new TypeAdapterFactory() Overridepublic TypeAdapter create(Gson gson, TypeToken type) return null;).create();四、JsonAdapter注解JsonAdap
17、ter相较之前介绍的SerializedName、FieldNamingStrategy、Since、Until、Expos这几个注解都是比较特殊的,其它的几个都是用在POJO的字段上,而这一个是用在POJO类上的,接收一个参数,且必须是TypeAdpater,JsonSerializer或JsonDeserializer这三个其中之一。上面说JsonSerializer和JsonDeserializer都要配合GsonBuilder.registerTypeAdapter使用,但每次使用都要注册也太麻烦了,JsonAdapter就是为了解决这个痛点的。使用方法(以User为例):123456
18、789101112131415161718JsonAdapter(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;Serializ
19、edName(value = emailAddress)public String email;使用时不用再使用GsonBuilder去注册UserTypeAdapter了。注:JsonAdapter仅支持TypeAdapter或TypeAdapterFactory12345Gson gson = new Gson();User user = new User(怪盗kidou, 24, ikidou);System.out.println(gson.toJson(user);/结果:name:怪盗kidou,age:24,email:ikidou/为区别结果,特意把email字段与Serial
20、izedName注解中设置的不一样注意:JsonAdapter的优先级比GsonBuilder.registerTypeAdapter的优先级更高。五、TypeAdapter与 JsonSerializer、JsonDeserializer对比TypeAdapterJsonSerializer、JsonDeserializer引入版本2.01.xStream API支持不支持*,需要提前生成JsonElement内存占用小比TypeAdapter大效率高比TypeAdapter低作用范围序列化和反序列化序列化或反序列化六、TypeAdapter实例注:这里的TypeAdapter泛指TypeA
21、dapter、JsonSerializer和JsonDeserializer。这里的TypeAdapter 上面讲了一个自动将 字符串形式的数值转换成int型时可能出现 空字符串的问题,下面介绍一个其它读者的需求:服务器返回的数据中data字段类型不固定,比如请求成功data是一个List,不成功的时候是String类型,这样前端在使用泛型解析的时候,怎么去处理呢?其实这个问题的原因主要由服务器端造成的,接口设计时没有没有保证数据的一致性,正确的数据返回姿势:同一个接口任何情况下不得改变返回类型,要么就不要返,要么就返空值,如null、,。但这里还是给出解决方案:方案一:12345678910
22、111213Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(List.class, new JsonDeserializerList() Overridepublic List deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException if (json.isJsonArray()/这里要自己负责解析了Gson newGson = new Gson();return newG
23、son.fromJson(json,typeOfT);else /和接口类型不符,返回空Listreturn Collections.EMPTY_LIST;).create();方案二:12345678910111213141516171819Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(List.class, new JsonDeserializerList() Overridepublic List deserialize(JsonElement json, Type typeOfT, JsonDeserializat
24、ionContext context) throws JsonParseException if (json.isJsonArray() JsonArray array = json.getAsJsonArray();Type itemType = (ParameterizedType) typeOfT).getActualTypeArguments()0;List list = new ArrayList();for (int i = 0; i array.size(); i+) JsonElement element = array.get(i);Object item = context
25、.deserialize(element, itemType);list.add(item);return list; else /和接口类型不符,返回空Listreturn Collections.EMPTY_LIST;).create();要注意的点: 必须使用registerTypeHierarchyAdapter方法,不然对List的子类无效,但如果POJO中都是使用List,那么可以使用registerTypeAdapter。 对于是数组的情况,需要创建一个新的Gson,不可以直接使用context,不然gson又会调我们自定义的JsonDeserializer造成递归调用,方案二没有重新创建Gson,那么就需要提取出List中E的类型,然后分别反序列化
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1