JsonDeserializer——Gson自定义解析类型错误的字段
来源:互联网 发布:qq群发信息软件 编辑:程序博客网 时间:2024/05/22 17:34
在开发中,定义好实体类和相应字段,Gson就可以很方便地帮助我们实现序列化和反序列化。
可是有时候,后台传给客户端的json数据格式有误,其中的某些字段类型错误,即,和我们在实体类中定义的字段类型不一致,此时就会出现类型转换错误,app原地爆炸!
假设有这么一个类Phone
代表手机:
/** * Author: Sbingo * Date: 2017/4/23 */public class Phone { /** * 手机大小 */ private Size size; /** * 手机内app列表 */ private List<App> apps; public Size getSize() { return size; } public void setSize(Size size) { this.size = size; } public List<App> getApps() { return apps; } public void setApps(List<App> apps) { this.apps = apps; }}
其中类Size
如下:
/** * Author: Sbingo * Date: 2017/4/23 */class Size { private String length; private String width; private String height; public String getLength() { return length; } public void setLength(String length) { this.length = length; } public String getWidth() { return width; } public void setWidth(String width) { this.width = width; } public String getHeight() { return height; } public void setHeight(String height) { this.height = height; } }
类App
如下:
/** * Author: Sbingo * Date: 2017/4/23 */class App { private String appName; private String developer; public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public String getDeveloper() { return developer; } public void setDeveloper(String developer) { this.developer = developer; } }
对于类Phone
,实际中我碰到的数据类型错误情况有:
对象size变成了字符串;
数组apps变成了字符串;
下面将会针对这两种错误分别讨论,其他错误可以类似处理。
当然这里的字符串是带有转义字符的json字符串,还是包含正确信息的。因此以下对字符串的处理可能只适应这种错误情况。
如何解决这一问题?
最好的办法当然是后台确保数据类型正确,从源头上消灭问题。
作为客户端开发,我们也要做好错误处理,应对各种场景。
客户端怎么处理?
registerTypeAdapter
其实Gson允许我们自定义解析某种类型的数据,一般我们使用Gson可能就直接new出来一个,但如果这样实例化:
Gson gson = new GsonBuilder() .registerTypeAdapter(A.class, new ADeserializer()) .registerTypeAdapter(B.class, new BDeserializer()) .create()
得到的gson就会使用自定义的ADeserializer
和BDeserializer
分别反序列化类A和类B。
`registerTypeAdapter 方法用于配置Gson的序列化或反序列化,接收两个参数。
第一个参数是注册的类型,即希望自定义解析的数据类型。
第二个参数包含了自定义的解析过程,它必须至少是TypeAdapter
、InstanceCreator
、JsonSerializer
、JsonDeserializer
四者之一的实现。
JsonDeserializer
泛型接口JsonDeserializer
中的方法deserialize
允许我们自定义反序列化过程,返回相对应的对象。
该方法接收三个参数,Gson会在进行相应类型字段的反序列化时回调该方法。
第一个参数类型为JsonElement,其中包含了真实返回的数据,它是一个抽象类,可以是JsonObject
、JsonArray
、JsonPrimitive
、JsonNull
四者之一,看名字也能知道这四者的大致类型。
第二个参数是当前反序列化的数据类型。
第三个是上下文参数context
。
在自定义反序列化时,我们要分别处理数据类型正确和错误的情况,具体处理过程视数据而定,以下仅供参考。
错误一:对象size变成了字符串
字段size为Size对象类型,当后台接口返回了字符串时,可以只对Size类型自定义反序列化,如下:
/** * Author: Sbingo * Date: 2017/4/23 */public class SizeDeserializer implements JsonDeserializer<Size> { @Override public Size deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { Size size = new Size(); if (json.isJsonObject()) { //类型正确 JsonObject jsonObject = json.getAsJsonObject(); size.setLength(jsonObject.get("length").getAsString()); size.setWidth(jsonObject.get("width").getAsString()); size.setHeight(jsonObject.get("height") == null ? "" : jsonObject.get("height").getAsString()); } else { //类型错误 String value = json.getAsString(); size = new Gson().fromJson(value, Size.class); } return size; }}
泛型参数传入Size,当反序列化Size对象时,就会回调我们自定义的方法。
首先用isJsonObject()
判断是否为对象
如果是,说明类型正确,接着依次设置各字段值。如果有些字段接口可能不会返回,记得判空,例如字段height
。
如果类型错误,我遇到的错误情况只会是带有转义字符的字符串,所以直接按此处理了。调用json.getAsString()
就能得到正确的json字符串,再通过Gson直接转换为Size类型。
这样不管接口返回数据中字段size
类型正确与否,都能从容应对!
错误二:数组apps变成了字符串
字段apps是一个list,接口应该返回一个数组,如果类型错误,可以有两种方式来自定义解析,在我看来,这两种各有优劣,具体使用要结合实际。
自定义解析字段本身
这种方法和解析size
类似,都是本着“谁类型错了,就解析谁”的原则。
由于没有找到用List
作为泛型参数的方法,所以需要把类Phone
中的字段apps
从list改成数组,如果真只能改成数组,使用方便性可能较list略差。
定义AppDeserializer
如下:
/** * Author: Sbingo * Date: 2017/4/23 */public class AppDeserializer implements JsonDeserializer<App[]> { @Override public App[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { if (json.isJsonArray()) { //类型正确 JsonArray jsonArray = json.getAsJsonArray(); App[] apps = new App[jsonArray.size()]; for (int i = 0; i < jsonArray.size(); i++) { App app = new App(); //获取app方法一 JsonObject jsonObject = jsonArray.get(i).getAsJsonObject(); app.setAppName(jsonObject.get("appName") == null ? "" : jsonObject.get("appName").getAsString()); app.setDeveloper(jsonObject.get("developer") == null ? "" : jsonObject.get("developer").getAsString()); //获取app方法二// app = context.deserialize(jsonObject, App.class); apps[i] = app; } return apps; } else if (json.isJsonObject()) { //类型错误1 return null; } else if (json.isJsonPrimitive()) { //类型错误2,多为String String value = ""; try { value = json.getAsString(); } catch (Exception e) { e.printStackTrace(); } if ("".equals(value)) { return null; } else { App[] apps = new Gson().fromJson(value, App[].class); return apps; } } else { //一般不出现这种情况 return null; } }}
泛型参数传入数组,当反序列化App类型的数组时,就会回调我们自定义的方法。
首先用isJsonArray()
类型是否正确,正确时获取数组也有两种方法,如代码所示,
方法一和之前的分析差不多。
方法二注意context
反序列化时要避免进入死循环。
类型错误时做了较多区分,具体还是要看实际情况。
自定义解析字段所在类
这种方法以错误字段apps所在类Phone为处理类型,好处是字段apps还是可以用list,但如果类中很多其他字段,工作量就大了一些,幸好此处只还有一个size。
定义AppListDeserializer
如下:
/** * Author: Sbingo * Date: 2017/4/23 */public class AppListDeserializer implements JsonDeserializer<Phone> { @Override public Phone deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { Phone phone = new Phone(); JsonObject jsonObject = json.getAsJsonObject(); //此时不考虑size的类型错误情况 if (jsonObject.get("size") != null) { jsonObject = jsonObject.get("size").getAsJsonObject(); Size size = context.deserialize(jsonObject, Size.class); phone.setSize(size); } JsonElement appsArray = jsonObject.get("apps"); if (appsArray != null) { List<App> apps = new ArrayList<>(); if (appsArray.isJsonArray()) { //类型正确 JsonArray jsonArray = appsArray.getAsJsonArray(); for (int i = 0; i < jsonArray.size(); i++) { JsonObject jo = jsonArray.get(i).getAsJsonObject(); App app = context.deserialize(jo, App.class); apps.add(app); } } else { //类型错误 String value = appsArray.getAsString(); App[] a = new Gson().fromJson(value, App[].class); apps = Arrays.asList(a); } phone.setApps(apps); } return phone; }}
泛型参数传入Phone,当反序列化Phone类型数据时,就会回调我们自定义的方法。
因为这里处理的是字段apps
,所以不考虑字段size
的类型正确与否。
Phone可以确定是个对象,所以直接用getAsJsonObject()
方法获取一个JsonObject
。
之后分别设置字段size
和apps
,其中用到的一些方法和上面类似。
JsonDeserializer自定义反序列化的思路大致就是这样,以后处理类似错误自当666。
- JsonDeserializer——Gson自定义解析类型错误的字段
- Google Gson:JsonSerializer 和 JsonDeserializer
- Gson指定类型的解析
- Gson解析动态字段
- Gson特殊字段解析
- Gson 基础教程 —— 自定义类型适配器(TypeAdapter)
- 自定义类型适配器的Gson工具类
- Gson解析 解析包含有data字段的json数据
- 自定义GSON类型适配器
- JSON解析之-Gson常见的错误
- Gson解析数据为null的错误
- Gson解析复杂类型
- MOSS2007的自定义字段类型
- Gson解析错误
- Gson解析JSON中动态未知字段key的方法
- Gson解析 数据源某个字段有时不存在的话的方法
- Gson解析JSON中动态未知字段key的方法
- Gson解析date类型 范例
- XTU 1250 Super Fast Fourier Transform
- HDU 1026 Ignatius and the Princess I(广搜+优先队列)
- zzuli 2127 tmk射气球
- JSP_9th_读写JavaBean
- 特征点匹配——ORB算法介绍
- JsonDeserializer——Gson自定义解析类型错误的字段
- C#之数组定义
- H
- CAPI-PSLSE 在linux环境下的编译
- 栈的应用--算术表达式的求值(中缀转后缀然后计算后缀表达式的值)
- 正方形
- JStorm Found multiple defaults.yaml resources. You're probably bundling the Storm jars with your top
- 分组背包
- 项目需求 找到产品的核心价值