和服务端小伙伴联调接口时总会遇到json格式的问题,导致客户端无法正常解析数据,出现数据解析异常。本文从客户端角度出发规避解析数据异常,避免重复字段和空指针异常,减少服务端小伙伴的工作量,增加客户端的稳定性
一,有数据时和无数据时返回数据类型不一致,一般发生在json对象和json数组
{
"name": "张三",
"age": 22,
"avatar": null,
"isRegister": false,
"strings": ["12312", "哈哈", "3213"] 有数据时
}
{
"name": "张三",
"age": 22,
"avatar": null,
"isRegister": false,
"strings": {} 无数据时
}
此时客户端解析json时strings字段就会数据类型异常,有数据时是json数组,无数据时是json对象。还有一种类似情况如下
{
"name": "张三",
"age": 22,
"avatar": { 有数据时
"large": "https://pic4.zhimg.com/v2-e552164d8e4ad9beca6eb50140df75b5_b.jpg",
"small": "https://pic4.zhimg.com/v2-e552164d8e4ad9beca6eb50140df75b5_b.jpg"
},
"isRegister": false,
"strings": ["12312", "哈哈", "3213"]
}
{
"name": "张三",
"age": 22,
"avatar": [], 无数据时
"isRegister": false,
"strings": ["12312", "哈哈", "3213"]
}
二,返回非数值类型时解析数据异常。
{
"name": "张三",
"age": "22", 字符串类型
"avatar": null,
"isRegister": false,
"strings": ["12312", "哈哈", "3213"]
}
{
"name": "张三",
"age": 22, 整型类型
"avatar": null,
"isRegister": false,
"strings": ["12312", "哈哈", "3213"]
}
{
"name": "张三",
"age": 22.5, 浮点类型
"avatar": null,
"isRegister": false,
"strings": ["12312", "哈哈", "3213"]
}
对于age字段就应该是一个整数类型,第一种返回一个字符串“22”,第三种返回浮点数22.5,客户端都会类型强转异常。
三,布尔类型数据解析异常
{
"name": "张三",
"age": 22,
"avatar": null,
"isRegister": false, 布尔类型
"strings": ["12312", "哈哈", "3213"]
}
{
"name": "张三",
"age": 22,
"avatar": null,
"isRegister": "false", 字符串类型
"strings": ["12312", "哈哈", "3213"]
}
{
"name": "张三",
"age": 22,
"avatar": null,
"isRegister": 0, 数值类型
"strings": ["12312", "哈哈", "3213"]
}
对于isRegister字段就应该是一个布尔类型,第二种返回一个字符串“false”,第三种返回整数类型0,客户端都会类型强转异常。
四,同一字段在不同接口返回的key不同
{
"id": 212, 用户ID
"age": 22,
"avatar": null,
"isRegister": false,
"strings": ["12312", "哈哈", "3213"]
}
{
"uid": 212, 这也是用户ID
"age": 22,
"avatar": null,
"isRegister": "false",
"strings": ["12312", "哈哈", "3213"]
}
{
"user_id": 212, 这还是用户ID
"age": 22,
"avatar": null,
"isRegister": 0,
"strings": ["12312", "哈哈", "3213"]
}
对于用户ID这个字段时而返回id、时而uid、时而user_id,增加客户端解析难度和误解,如果不慎取错就只能是默认值0或者“”或者null,业务执行逻辑就会错误或者崩溃
五,json数组中返回null的对象,这是最可怕的一种,线上已经验证过。
{
"name": "张三",
"age": 22,
"avatar": null,
"isRegister": false,
"strings": null 会验证空指针,客户端不会崩溃
}
{
"name": "张三",
"age": 22,
"avatar": null,
"isRegister": "false",
"strings": [] 会验证数组长度,客户端不会崩溃
}
{
"name": "张三",
"age": 22,
"avatar": null,
"isRegister": 0,
"strings": ["哈哈", null, "3213"] 不会验证数组中的顶级对象是否为null,客户端会崩溃
}
对于json数组返回null或者[],客户端都会验证不会发生崩溃,如果返回["哈哈", null, "3213"],因为客户端不会验证数组中的顶级对象是否为null,所以会崩溃
针对上述五种异常,客户端分别对应五种解决方式。服务端小伙伴再也不用不厌其烦的改动,客户端一处配置处处生效,无需重复代码,避免解析异常,减少崩溃。
一,有数据时和无数据时返回数据类型不一致 and
json数组中返回null的对象
(1)针对json对象 registerTypeAdapterFactory(new ObjectTypeAdapterFactory())
GsonBuilder,注册ObjectTypeAdapterFactory对象,代理解析json对象
try {
JsonElement tree = elementAdapter.read(in);
if (delegate instanceof Adapter) {
if (!tree.isJsonObject()) { 判断是否是json对象
return null; 否?返回默认null,执行下一字段解析
} else {
return delegate.fromJsonTree(tree); 是?正常解析数据
}
} else {
return delegate.fromJsonTree(tree);
}
} catch (Exception e) {
e.printStackTrace();
return null; 抓住发生的异常,返回默认值null,执行下一字段解析
}
(2)针对json数组registerTypeAdapter(List.class, new ListDeserializer())
GsonBuilder,注册ListDeserializer对象,代理解析json数组
if (json.isJsonArray()) { 判断是否是json数组
Gson newGson = new Gson();
List> list = newGson.fromJson(json, typeOfT); 是?正常解析数据
if (list == null) return Collections.EMPTY_LIST; 解析数组为null ? 返回默认空数组,执行下一字段解析
list.removeAll(Collections.singleton(null)); 数组为其中顶级元素为null ? 移除null元素,改变数组长度
return list;
} else {
return Collections.EMPTY_LIST; 否?返回默认空数组,执行下一字段解析
}
二,返回非数值类型时解析数据异常
registerTypeAdapter(Integer.class, new IntegerDeserializer())
registerTypeAdapter(Long.class, new LongDeserializer())
registerTypeAdapter(Float.class, new FloatDeserializer())
registerTypeAdapter(Double.class, new DoubleDeserializer())
GsonBuilder,注册对应的数值类型Deserializer对象,代理解析数值类,分别解析整型,长整型,单精度浮点型,双精度浮点型
try {
return (int) json.getAsJsonPrimitive().getAsDouble(); 默认用双精度浮点型解析在转换为想要的类型
} catch (Exception e) {
e.printStackTrace();
return 0; 出现异常返回默认值0,执行下一字段解析
}
三,布尔类型数据解析异常
registerTypeAdapter(Boolean.class, new BooleanSerializer())
GsonBuilder,注册BooleanSerializer对象,代理解析布尔类型
try {
if (json.getAsJsonPrimitive().isNumber()) { 是否返回的是数值类型
return json.getAsJsonPrimitive().getAsDouble() != 0F; 是 ? 数值 == 0 时 为 false 非 0 为 true
} else {
return json.getAsJsonPrimitive().getAsBoolean(); 否直接解析布尔值
}
} catch (Exception e) {
e.printStackTrace();
return false; 出现异常返回默认值false,执行下一字段解析
}
四,同一字段在不同接口返回的key不同
此种情况需要为单独的解析实体类配置。例如搜索接口返回的结果,@SerializedName
可配置多种字段,客户端只用一个字段接收,任你服务端千变万化,我自客户端岿然不动。
@SerializedName(value = "title", alternate = {"session_name"})
public String title;
@SerializedName(value = "logo_cover", alternate = {"image_phone"})
public String logo_cover;
public String logo_detail;
public int calorie;
@SerializedName(value = "sessionCount", alternate = {"session_count"})
public int sessionCount;
@SerializedName(value = "peopleCount", alternate = {"downloads"})
public int peopleCount;
public String category_name;
public int content_type;
@SerializedName(value = "id", alternate = {"programId", "sessionId"})
public int id;
总结
Q1,一个字段时而有时而没有,客户端会出现异常吗?
不会异常不会崩溃,只要不影响业务逻辑,服务端怎么简单怎么来。
Q2,一个字段返回的类型和客户端定义的类型不一致,能正常解析道数据吗?默认值分别是什么?
-
数值类型/字符串类型对应json各种格式
{ "age":22, 数值类型 "age":"22", 字符串类型 "age":{}, 布尔类型 "age":{}, 对象类型 "age":[], 数组类型 }
json数值类型和json字符串类型都能正确解析数据,其他类型会忽略解析异常,数值类型默认值0,字符串类型默认值为“”。
-
布尔类型对应json各种格式
{ "isRegister":0, 数值类型 "isRegister":false 布尔类型 "isRegister":"false", 字符串类型布尔值 "isRegister":"哈哈", 字符串类型非布尔值 "isRegister":{}, 对象类型 "isRegister":[], 数组类型 }
json数值类型时,0为false非0为true。json布尔类型和json字符串类型布尔值都能正确解析数据。其他类型会忽略解析异常,数值类型默认值false。
-
对象和数组
{ "avatar":{}, 对象类型 "avatar":[], 数组类型 }
对象类型和数组类型只有一一对应才能解析道正确数组,其他类型会忽略解析异常,对象默认值为null,数组默认值未空数组。
服务端小伙伴以后再也不用问我,字段不返回有没有什么问题,我再也不用叮嘱服务端大括号改中括号,中括号改大括号的问题了,完结撒花。
最后"null" 和 null
区别,加了""
就是字符串和里面包什么内容没有什么关系,null
表明返回的是一个null
对象,和客户端对象变量的默认值是一样的。
源码:https://github.com/YougaKing/GosnFormat.git