本文为作者根据日常使用结合Gson源码注释及wiki所作的原创内容,转载请注明出处。
该系列其它文章
注:此系列基于Gson 2.4。
本次文章的主要内容:
- TypeAdapter
- JsonSerializer与JsonDeserializer
- TypeAdapterFactory
- @JsonAdapter注解
- TypeAdapter与 JsonSerializer、JsonDeserializer对比
- TypeAdapter实例
- 结语
- 后期预告
一、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
必须使用包装类型,所以int.class
,long.class
,float.class
和double.class
是行不通的。同时不能使用父类来替上面的子类型,这也是为什么要分别注册而不直接使用Number.class
的原因。
上面特别说明了registerTypeAdapter
不行,那就是有其它方法可行咯?当然!换成**registerTypeHierarchyAdapter
**就可以使用Number.class
而不用一个一个的当独注册啦!
registerTypeAdapter与registerTypeHierarchyAdapter的区别:
registerTypeAdapter | registerTypeHierarchyAdapter | |
---|---|---|
支持泛型 | 是 | 否 |
支持继承 | 否 | 是 |
注:如果一个被序列化的对象本身就带有泛型,且注册了相应的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注解中设置的不一样
注意:JsonAdapter
的优先级比GsonBuilder.registerTypeAdapter
的优先级更高。
五、TypeAdapter与 JsonSerializer、JsonDeserializer对比
TypeAdapter | JsonSerializer、JsonDeserializer | |
---|---|---|
引入版本 | 2.0 | 1.x |
Stream API | 支持 | 不支持*,需要提前生成JsonElement
|
内存占用 | 小 | 比TypeAdapter 大 |
效率 | 高 | 比TypeAdapter 低 |
作用范围 | 序列化 和 反序列化 | 序列化 或 反序列化 |
六、TypeAdapter实例
注:这里的TypeAdapter泛指TypeAdapter
、JsonSerializer
和JsonDeserializer
。
这里的TypeAdapter 上面讲了一个自动将 字符串形式的数值转换成int型时可能出现 空字符串的问题,下面介绍一个其它读者的需求:
服务器返回的数据中data字段类型不固定,比如请求成功data是一个List,不成功的时候是String类型,这样前端在使用泛型解析的时候,怎么去处理呢?
其实这个问题的原因主要由服务器端造成的,接口设计时没有没有保证数据的一致性,正确的数据返回姿势:同一个接口任何情况下不得改变返回类型,要么就不要返,要么就返空值,如null
、[]
,{}
。
但这里还是给出解决方案:
方案一:
Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(List.class, new JsonDeserializer<List<?>>() {
@Override
public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonArray()){
//这里要自己负责解析了
Gson newGson = new Gson();
return newGson.fromJson(json,typeOfT);
}else {
//和接口类型不符,返回空List
return Collections.EMPTY_LIST;
}
}
}).create();
方案二:
Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(List.class, new JsonDeserializer<List<?>>() {
@Override
public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext 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.deserialize(element, itemType);
list.add(item);
}
return list;
} else {
//和接口类型不符,返回空List
return Collections.EMPTY_LIST;
}
}
}).create();
要注意的点:
- 必须使用
registerTypeHierarchyAdapter
方法,不然对List的子类无效,但如果POJO中都是使用List,那么可以使用registerTypeAdapter
。 - 对于是数组的情况,需要创建一个新的Gson,不可以直接使用context,不然gson又会调我们自定义的
JsonDeserializer
造成递归调用,方案二没有重新创建Gson,那么就需要提取出List<E>中E的类型,然后分别反序列化适合为E手动注册了TypeAdaper的情况。 - 从效率上推荐方案二,免去重新实例化Gson和注册其它TypeAdapter的过程。
结语
Gson系列总算是完成了,感觉写得越来越差了,我怕我写得太啰嗦,也不能总是大片大片的贴代码,所以可能有的地方写得并不详细,排版也不美观,但都些都不重点,重点是Gson里我们能用上的都一一介绍一遍,只要你确确实实把我这几篇文章上的内容都学会的话,以后Gson上的任何问题都不再是问题,当然可能很多内容对于实际的开发中用的并不多,但下次有什么疑难杂症就难不倒你了。
本系列不提供Demo源码,最重要的是自己实验。
写一篇文章还是要花不少时间和精力,要写示例、调式、组织语言、码字等等,加上关注的人在慢慢的增加的同时既给了我动力也给我不少压力,如有纰漏或者更好的例子都可以和我交流。
后期预告:
之前有人给我评论说 出一点 retrofit 相关内容,我想了想,出是会出,但在此之前我想先出大概3~4篇文章用于介绍 泛型、反射、注解和HTTP 的相关内容,当你确实掌握之后,我打包票你只需要看一遍Retrofit官方教程的代码示例,都不用看其它英文说明,就可以轻松玩转Retrofit。不服来战!
网友评论
||TypeAdapter| JsonSerializer、JsonDeserializer|
|--|--|--|--|
|引入版本|2.0|1.x|
|Stream API|支持|不支持*,需要提前生成JsonElement|
|内存占用|小|比TypeAdapter大|
|效率|高|比TypeAdapter低|
|作用范围|序列化 和 反序列化|序列化 或 反序列化|
public class TypeSuperDeserializer implements JsonDeserializer<TypeSuper> {
private static final String HorizontalScrollCard = "horizontalScrollCard";
private static final String Video = "video";
private static final String TextFooter = "textFooter";
private static final String VideoCollectionWithCover = "videoCollectionWithCover";
private static final String TextHeader = "textHeader";
private static final String VideoCollectionOfFollow = "videoCollectionOfFollow";
@Override
public TypeSuper deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)throws JsonParseException{
JsonObject jsonObject = json.getAsJsonObject();
//获取里面的type字段,用作区分的标志
String type = jsonObject.get("type").getAsString();
if (type.equals(HorizontalScrollCard))
return new Gson().fromJson(json, HorizontalScrollCard.class);
if (type.equals("video"))
return new Gson().fromJson(json, Video.class);
if (type.equals(TextFooter))
return new Gson().fromJson(json, TextFooter.class);
if (type.equals(VideoCollectionWithCover))
return new Gson().fromJson(json, VideoCollectionWithCover.class);
if (type.equals(TextHeader))
return new Gson().fromJson(json, TextHeader.class);
if (type.equals(VideoCollectionOfFollow))
return new Gson().fromJson(json, VideoCollectionOfFollow.class);
else
return null;
}
}
但是具体怎么区分并使用return new Gson().fromJson(json, xxx.class);的不同数据呢?
有个问题请大神指教一下
pojo如下定义:
public class User {
public String name;
public int age;
public String emailAddress;
public String phone;
}
server端返回json数据,可能出于各种原因,返回的字段少了,比如少了phone,或少了name,像这样:
{
"name": "怪盗kidou",
"age": 24,
"emailAddress": "ikidou_1@example.com"
}
这种情况假设说是不确定的,任意一个接口,任意一个字段都可能少传
那么反序列化完之后,在调用pojo对象的时候,比如user.phone或user.name就会抛空指针异常,这样总不能取每个属性的时候都校验一下吧?
4篇文章都看了,但是没找到解决办法,也可能有但是鄙人愚钝并未发现,希望司机大哥看到了帮忙解答一下吧,不胜感激~~~
如果真是有约定好的某个非空字段server端未返回,那么要从问题根源上处理,也就是server端修改
{
"phone":null
}
这种的话就没有办法了,gson没有提供默认值或者校验的API
是这样,如果我有两个对象 比如说是 A 和 B,A 和 B 中都有 各自的成员变量,我如果将这两个对象合并为一个json呢?
比如 A 中有 String name,String pwd ;
B 中有 String color,String size;
我如何得到
{
"name":"google",
"pwd":"123",
"color","red",
"size","20"
}
或者还是我的思路不对,应该用其他的方式来实现?新手来请教一下,打扰到您,请见谅
{
"name":"google",
"pwd":"123",
"color","red",
"size","20"
}
这样的形式啊? Gson 中好像没有合并的方法,只能够自己拼接吗? 太菜了,求教一哈
"code": 10000,
"msg": "成功",
"data": { }
};当请求失败,返回{
"code": 10001,
"msg": "失败",
"data": []
}; 针对这样的情况,怎么做解析的封装?
第二部分
@SuppressWarnings("unchecked")
@Override
public T read(JsonReader in) throws IOException {
if( in.peek().equals(JsonToken.BEGIN_ARRAY)){
List<User> list = new ArrayList<User>();
in.beginArray();
while (in.hasNext()) {
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.emailAddress = in.nextString();
break;
}
}
in.endObject();
list.add(user);
}
in.endArray();
return (T) list;
}else{
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.emailAddress = in.nextString();
break;
}
}
in.endObject();
return (T) user;
}
}
}
第一部分
package com.ueuo.gson;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
public class CustomTypeAdapter<T> extends TypeAdapter<T>{
@SuppressWarnings("unchecked")
@Override
public void write(JsonWriter out, T paramT) throws IOException {
if(paramT instanceof User){
out.beginObject();
User user = (User)paramT;
out.name("name").value(user.name);
out.name("age").value(user.age);
out.name("email").value(user.emailAddress);
out.endObject();
}else if(paramT instanceof List<?>){
List<User> users = (List<User>)paramT;
out.beginArray();
for (User value : users) {
out.beginObject();
out.name("name").value(value.name);
out.name("age").value(value.age);
out.name("email").value(value.emailAddress);
out.endObject();
}
out.endArray();
}
}
第七部分:
System.out.println("\n方案二:");
Gson gson10 = new GsonBuilder().registerTypeHierarchyAdapter(List.class, new JsonDeserializer<List<?>>() {
@Override
public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext 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.deserialize(element, itemType);
list.add(item);
}
return list;
} else {
//和接口类型不符,返回空List
return Collections.EMPTY_LIST;
}
}
}).create();
List<User> users10 = gson10.fromJson(result2, type);
for(User user10 : users10){
System.out.println(user10.name+"; "+user10.age+"; "+user10.emailAddress);
}
List<User> users11 = gson10.fromJson("\"\"", type);
System.out.println(users11);
}
}
第六部分:
System.out.println("\n四、@JsonAdapter注解");
System.out.println("\n使用@JsonAdapter注解");
Gson gson7 = new Gson();
User7 user7 = new User7("怪盗kidou", 24, "ikidou@example.com");
System.out.println(gson7.toJson(user7));
System.out.println("\n六、TypeAdapter实例");
Gson gson8 = new GsonBuilder().registerTypeHierarchyAdapter(List.class, new JsonDeserializer<List<?>>() {
@Override
public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonArray()){
//这里要自己负责解析了
Gson newGson = new Gson();
return newGson.fromJson(json,typeOfT);
}else {
//和接口类型不符,返回空List
return Collections.EMPTY_LIST;
}
}
}).create();
System.out.println("\n方案一:");
List<User> users8 = gson8.fromJson(result2, type);
for(User user8 : users8){
System.out.println(user8.name+"; "+user8.age+"; "+user8.emailAddress);
}
List<User> users9 = gson8.fromJson("\"\"", type);
System.out.println(users9);
第五部分:
System.out.println("\n三、TypeAdapterFactory");
System.out.println("\n自定义泛型TypeAdapter -- CustomTypeAdapter<T>");
Gson gson6 = new GsonBuilder()
.registerTypeAdapterFactory(new TypeAdapterFactory() {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
return new CustomTypeAdapter<T>();
}
})
.create();
List<User> list2 = new ArrayList<User>();
list2.add(new User("a", 11));
list2.add(new User("b", 22));
System.out.println("\n序列化");
String result2 = gson6.toJson(list2, type);
System.out.println(result2);
System.out.println("\n反序列化");
users = gson6.fromJson(result2, type);
for (User u : users) {
System.out.println(u.name+"; "+u.age+"; "+u.emailAddress);
}
System.out.println("\n序列化");
String result3 = gson6.toJson(new User("c", 33), User.class);
System.out.println(result3);
System.out.println("\n反序列化");
User user3 = gson6.fromJson(result3,User.class);
System.out.println(user3.name+"; "+user3.age+"; "+user3.emailAddress);
第四部分:
@Override
public List<User> read(JsonReader in) throws IOException {
List<User> list = new ArrayList<User>();
in.beginArray();
while (in.hasNext()) {
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.emailAddress = in.nextString();
break;
}
}
in.endObject();
list.add(user);
}
in.endArray();
return list;
}
};
Gson gson5 = new GsonBuilder()
.registerTypeAdapter(type, typeAdapter)
.create();
List<User> list = new ArrayList<User>();
list.add(new User("a", 11));
list.add(new User("b", 22));
// 注意,多了个type参数
System.out.println("如果一个被序列化的对象本身就带有泛型,且注册了相应的TypeAdapter,\n那么必须调用Gson.toJson(Object,Type),明确告诉Gson对象的类型。");
System.out.println("\n序列化");
String result = gson5.toJson(list, type);
System.out.println(result);
System.out.println("\n反序列化");
List<User> users = gson5.fromJson(result, new TypeToken<List<User>>() {}.getType());
for (User u : users) {
System.out.println(u.name+"; "+u.age+"; "+u.emailAddress);
}
第三部分:
System.out.println("\n所有数字都转成序列化为字符串的例子");
JsonSerializer<Number> numberJsonSerializer = new JsonSerializer<Number>() {
@Override
public JsonElement serialize(Number src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(String.valueOf(src));
}
};
Gson gson4 = new GsonBuilder().registerTypeAdapter(Integer.class, numberJsonSerializer)
.registerTypeAdapter(Long.class, numberJsonSerializer)
.registerTypeAdapter(Float.class, numberJsonSerializer)
.registerTypeAdapter(Double.class, numberJsonSerializer).create();
System.out.println(gson4.toJson(100.0f));// 结果:"100.0"
System.out.println(gson4.toJson(100));// 结果:"100"
System.out.println(gson4.toJson(100.0d));// 结果:"100.0"
System.out.println(gson4.toJson(0x64));// 结果:"100"
System.out.println("\nTypeAdapter<List<User>>");
Type type = new TypeToken<List<User>>() {
}.getType();
TypeAdapter<List<User>> typeAdapter = new TypeAdapter<List<User>>() {
@Override
public void write(JsonWriter out, List<User> paramT) throws IOException {
out.beginArray();
for (User value : paramT) {
out.beginObject();
out.name("name").value(value.name);
out.name("age").value(value.age);
out.name("email").value(value.emailAddress);
out.endObject();
}
out.endArray();
}
第二部分:
System.out.println();
User user2 = gson.fromJson(stringjson, User.class);
System.out.println(user2.name);
System.out.println(user2.age);
System.out.println(user2.emailAddress);
System.out.println("\n注册一个TypeAdapter序列化和反序列化空字符串");
Gson gson2 = 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(gson2.toJson(100)); // 结果:"100"
System.out.println(gson2.fromJson("\"\"", Integer.class)); // 结果:-1
System.out.println("\n二、JsonSerializer与JsonDeserializer");
System.out.println("\n只接管反序列化的过程就用 JsonDeserializer");
Gson gson3 = 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(gson3.toJson(100)); // 结果:100
System.out.println(gson3.fromJson("\"\"", Integer.class)); // 结果-1
第一部分:
package com.ueuo.gson;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
/**
* 四、你真的会用Gson吗?Gson使用指南(四)
*
* http://www.jianshu.com/p/3108f1e44155
*
* @Author xbn 2017.11.21
*/
public class UsageGsonFour {
public static void main(String[] args) throws Exception {
System.out.println("\n一、TypeAdapter");
System.out.println("\n为User注册TypeAdapter");
User user = new User("怪盗kidou", 24);
user.emailAddress = "ikidou@example.com";
Gson gson = new GsonBuilder()
// 为User注册TypeAdapter
.registerTypeAdapter(User.class, new UserTypeAdapter()).create();
String stringjson = gson.toJson(user);
System.out.println(stringjson);
private static GsonBuilder gsonBulder = new GsonBuilder();
gsonBulder.registerTypeAdapter(java.sql.Timestamp.class, JsonUtil.TIMESTAMP);
public static final TypeAdapter<java.sql.Timestamp> TIMESTAMP = new TypeAdapter<java.sql.Timestamp>() {
@Override
public java.sql.Timestamp read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
try {
return new java.sql.Timestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(in.nextString()).getTime());
} catch (Exception e) {
throw new JsonSyntaxException(e);
}
}
@Override
public void write(JsonWriter out, java.sql.Timestamp value) throws IOException {
out.value(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(value));
}
};
希望对大家有帮助.
源码解析:http://tinyurl.com/ycqn86hj
惘
这我的GSON混淆
-keepattributes Signature
# For using GSON @Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson
#这句非常重要,主要是滤掉 com.demo.demo.bean包下的所有.class文件不进行混淆编译,com.demo.demo是你的包名
-keep class com.xxx.xxx.model** {*;}
-keepclassmembers class com.xxx.xxx.model** { <fields>; }
试了很多种还是报下面这个错误
at com.google.gson.internal.bind.TypeAdapters$26.a(SourceFile:762)
可以按Gson 官方的配置配,如果你用了 SerializedName 注解的话 就可以不用加 你的model包下的那些类
https://github.com/google/gson/blob/master/examples/android-proguard-example/proguard.cfg
正确的时候返回的是{ "result":0,"msg":"密码错误","data":{ } },
错误的时候返回的是{ "result":0,"msg":"密码错误","data":[ ] },
按你上面那个方法registerTypeHierarchyAdapter了但是,这个方法只走一次,就是最外层会走这个方法,里面解析data时它就不走这个方法了。
/** @Override public T read(JsonReader in) throws IOException {
if (deserializer == null) {
return delegate().read(in);
}
JsonElement value = Streams.parse(in);
if (value.isJsonNull()) {
return null;
}
return deserializer.deserialize(value, typeToken.getType(), context);
}**/
public static final TypeAdapter<StringBuilder> STRING_BUILDER = new TypeAdapter<StringBuilder>() {
@Override
public StringBuilder read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
return new StringBuilder(in.nextString());
}
帮忙了.
> 「当我们为User.class 注册了 TypeAdapter之后,只要是操作User.class 那些之前介绍的@SerializedName 、FieldNamingStrategy、Since、Until、Expos通通都黯然失色,失去了效果,只会调用我们实现的UserTypeAdapter.write(JsonWriter, User) 方法,我想怎么写就怎么写。」
这部分能详细说明一下吗?官方文档貌似没有提及这一点。
失效的问题对于JsonSerializer与JsonDeserializer也一样吗?
失效的范围是针对GsonBuilder的其他所有设置?
而针对自定义类型,使用TypeAdapter、JsonSerializer进行序列化时应该都是完全手工接管整个序列化过程,所以setDateFormat、Since、Expos之类的设定都没有任何意义,是这个意思?
// inner class
public class A {
public String a = "a";
class B {
public String b = "b";
public B() {
}
}
}
// test
public static void serialize() {
Gson gson = new Gson();
gson.toJson(new A().new B(), System.out); // {"b":"b"}
}
public static void deserialize() {
Gson gson = new Gson();
A.B deserialization = gson.fromJson("{\"b\":\"b\"}", A.B.class);
System.out.println("deserialization= " + deserialization);
}
但是解析过程中不会报错,有没有办法,让报错,或者 有为null 的自动过滤掉,不显示到返回得到的 list中?
2、让报错实现方式也简单。比如自行遍历java属性列表进行判空,有空时,抛出异常。
3、『不显示到返回得到的 list中』没有太明白
你的2种解决方案我没看太懂,方法里面的json 指的是 返回的json么?还是只指的返回的json里面的 data 的json?
if else if是返回有数据的list集合 而 else是返回 空集合,不是说为空是返回String字符串么?
beginObject: {
endObject: }
beginArray:[
endArray:]
hasNext:对象或数组中还没有其它元素
nextName:在对象中取出下一个json的键
nextString、nextInt、nextXXX:从json中取出值,并转换成相应的类型
skipValue:跳过取值,如果这个键你用不上,那么值也用不上,所以跳过,不然你用next的时候会有问题
其它的方法都是反射时使用比较多了,就这么几个方法,你尝试着用它手动解析一下,如果你试过之后觉得还有必要,那个时候我再写不迟。
{code:
msg:
data:{}
}
pojo中data用的泛型,现在gson解析泛型会了,但是在retrofit2.0中该怎么添加convertor呢?
.addConverterFactory(GsonConverterFactory.create())
.url(BASE_URL)
.build();
Gson好像不会调用set,我本打算在set里面设置,可是这样没效果。代码如下
public void setAvatar(String avatar) {
this.avatar = "http://www.baidu.com" + avatar;
}
public String getAvatar(){
if(!avatar.startWith("/")){ //或者判断是不是http开头
return "http://www.baidu.com"+avatar;
}
return avatar;
}
其它方法当然也行,只是能用这种方式解决就没必要用Gson来解决
某些 REST API 返回数组列表的时候,
会多加一层 META DATA,
例如
{
"meta":{
"page":0,
"total":10
},
"data":{
.....
}
}
有时候不需要知道 meta data 的 info,
所以不想多写一个 class 出来,
有除了 TypeAdapter 以外的做法来处理这种情形吗?
我明白可以使用泛型,
但是这样就要在多一个类,
有些专案实际上我是用不到 meta 的 资料的,
实在不想多一个 class,
所以除了这个方法以外,
没有其他方法了?
public class Meta{
public int page;
public int total;
public String XX;
public String YY;
}
如果每个接口都不一样,那你也可以像data一样用泛型啊,根本用不着用TypeAdapter
public class Result<M,T> {
public int code;
public String message;
public M meta;
public T data;
}