博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Gson 增加额外校验功能
阅读量:6968 次
发布时间:2019-06-27

本文共 19160 字,大约阅读时间需要 63 分钟。

Gson使用中遇到的几种情况

  1. 解析完数据,使用时经常需要对数据判空,以免后台返回数据格式有误导致客户端Crash,非常繁琐,且容易遗漏。
  2. 解析完数据除了判空,有时还要对一些字段有效性做判断(例如id有效)。能否在解析过程中直接过滤掉无效异常数据,用的时候不需要再判断呢?
  3. 接口返回的数据自动解析后,经常和最终需要的数据格式不完全一致,需要进行预处理。例如User的名称返回空串,则需要在客户端显示成“匿名”;再例如接口返回二维数组,而UI组件需要的是带有Type信息的一维数组来实现分组列表。

解析的数据免判空

修改数据实现类

在数据Model实现类中过滤掉null值。例如设计一个List,只有非null值才能被添加进去,取出Item时就不需要判空了。

可以封装一个ItemNonNullList,在ArrayList外面包一层,并特殊处理add/addAll相关方法,保证为null的Item不会被添加进去。不需要自定义TypeAdapter修改Gson解析过程。

public class ItemNonNullList
implements List
, RandomAccess, Cloneable, Serializable { @NotNull private final ArrayList
mList; public ItemNonNullList() { mList = new ArrayList<>(); } @SuppressWarnings("unchecked") private ItemNonNullList(@NotNull ItemNonNullList
other) { mList = (ArrayList
) other.mList.clone(); } @Override public int size() { return mList.size(); } @Override public boolean isEmpty() { return mList.isEmpty(); } @Override public boolean contains(Object o) { return mList.contains(o); } @NotNull @Override public Iterator
iterator() { return mList.iterator(); } @NotNull @Override public Object[] toArray() { return mList.toArray(); } @NotNull @Override public
T[] toArray(@NotNull T[] a) { return mList.toArray(a); } @Override public boolean add(E e) { return e != null && mList.add(e); } @Override public boolean remove(Object o) { return mList.remove(o); } @Override public boolean containsAll(@NotNull Collection
c) { return mList.containsAll(c); } @Override public boolean addAll(@NotNull Collection
c) { mList.ensureCapacity(mList.size() + c.size()); boolean result = false; for (E e : c) { result |= e != null && mList.add(e); } return result; } @SuppressWarnings("unchecked") @Override public boolean addAll(int index, @NotNull Collection
c) { ArrayList
list = null; for (Object o : c) { if (o != null) { if (list == null) { list = new ArrayList<>(c.size()); } list.add((E) o); } } return list != null && mList.addAll(index, list); } @Override public boolean removeAll(@NotNull Collection
c) { return mList.removeAll(c); } @Override public boolean retainAll(@NotNull Collection
c) { return mList.retainAll(c); } @Override public void clear() { mList.clear(); } @NotNull @Override public E get(int index) { return mList.get(index); } @Nullable @Override public E set(int index, E element) { return element == null ? null : mList.set(index, element); } @Override public void add(int index, E element) { if (element != null) { mList.add(index, element); } } @NotNull @Override public E remove(int index) { return mList.remove(index); } @Override public int indexOf(Object o) { return mList.indexOf(o); } @Override public int lastIndexOf(Object o) { return mList.lastIndexOf(o); } @NotNull @Override public ListIterator
listIterator() { return mList.listIterator(); } @NotNull @Override public ListIterator
listIterator(int index) { return mList.listIterator(index); } @NotNull @Override public List
subList(int fromIndex, int toIndex) { return mList.subList(fromIndex, toIndex); } @SuppressWarnings("MethodDoesntCallSuperMethod") @Override public ItemNonNullList
clone() { return new ItemNonNullList<>(this); }}复制代码

全局替换解析过程

对于特定类型,全局替换Gson解析过程。例如String类型null解析为空串"";数组、List解析为空数组、空List而不是null等。

以String为例,可以把TypeAdapters.STRING复制出来,并修改其中代码如下,将null解析为空字符串,然后注册到Gson中,覆盖String默认的TypeAdapter。

修改前:

public static final TypeAdapter
STRING = new TypeAdapter
() { @Override public String read(JsonReader in) throws IOException { JsonToken peek = in.peek(); if (peek == JsonToken.NULL) { in.nextNull(); return null; } /* coerce booleans to strings for backwards compatibility */ if (peek == JsonToken.BOOLEAN) { return Boolean.toString(in.nextBoolean()); } return in.nextString(); } @Override public void write(JsonWriter out, String value) throws IOException { out.value(value); }};复制代码

修改后:

public static final TypeAdapter
STRING = new TypeAdapter
() { public String read(JsonReader reader) { try { if (reader.peek() == JsonToken.NULL) { reader.nextNull(); return ""; // 原先是返回null,这里改为返回空字符串 } return reader.nextString(); } catch (Exception e) { e.printStackTrace(); } return ""; } public void write(JsonWriter writer, String value) { try { if (value == null) { writer.nullValue(); return; } writer.value(value); } catch (Exception e) { e.printStackTrace(); } }};复制代码

类似的,还可以覆盖Gson内置的TypeAdapters.INTEGER、CollectionTypeAdapterFactory、ArrayTypeAdapter等,实现Integer、Collection、数组等类型的免判空。

public static final TypeAdapter
INTEGER = new TypeAdapter
() { @Override public Number read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } try { return in.nextInt(); } catch (NumberFormatException e) { throw new JsonSyntaxException(e); } } @Override public void write(JsonWriter out, Number value) throws IOException { out.value(value); }};/** * 自定义adapter,解决由于数据类型为Int,实际传过来的值为Float,导致解析出错的问题 * 目前的解决方案为将所有Int类型当成Double解析,再强制转换为Int */public static final TypeAdapter
INTEGER = new TypeAdapter
() { @Override public Number read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return 0; } try { double i = in.nextDouble();//当成double来读取 return (int) i;//强制转为int } catch (NumberFormatException e) { throw new JsonSyntaxException(e); } } @Override public void write(JsonWriter out, Number value) throws IOException { out.value(value); }};复制代码

数组部分略麻烦,由于gson用以数组解析的Adapter是不可重写的,只好拷贝出来,重新写了个类。注意上面的TypeAdapterRuntimeTypeWrapper类不是public的,所以也得拷贝出来写一个到本地。

/** * 自定义CollectionTypeAdapterFactory,使json内的数组为null时,返回空数组而不是null对象 */public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {    private final ConstructorConstructor constructorConstructor;    public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor) {        this.constructorConstructor = constructorConstructor;    }    public 
TypeAdapter
create(Gson gson, TypeToken
typeToken) { Type type = typeToken.getType(); Class
rawType = typeToken.getRawType(); if (!Collection.class.isAssignableFrom(rawType)) { return null; } Type elementType = $Gson$Types.getCollectionElementType(type, rawType); TypeAdapter
elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType)); ObjectConstructor
constructor = constructorConstructor.get(typeToken); @SuppressWarnings({
"unchecked", "rawtypes"}) // create() doesn't define a type parameter TypeAdapter
result = new Adapter(gson, elementType, elementTypeAdapter, constructor); return result; } private static final class Adapter
extends TypeAdapter
> { private final TypeAdapter
elementTypeAdapter; private final ObjectConstructor
> constructor; public Adapter(Gson context, Type elementType, TypeAdapter
elementTypeAdapter, ObjectConstructor
> constructor) { this.elementTypeAdapter = new TypeAdapterRuntimeTypeWrapper
(context, elementTypeAdapter, elementType); this.constructor = constructor; } public Collection
read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); //这里做了修改,原先是返回null,改为返回空数组 return constructor.construct(); } Collection
collection = constructor.construct(); in.beginArray(); while (in.hasNext()) { E instance = elementTypeAdapter.read(in); collection.add(instance); } in.endArray(); return collection; } public void write(JsonWriter out, Collection
collection) throws IOException { if (collection == null) { out.nullValue(); return; } out.beginArray(); for (E element : collection) { elementTypeAdapter.write(out, element); } out.endArray(); } }}复制代码

进行注册:

static {        GsonBuilder gsonBulder = new GsonBuilder();        gsonBulder.registerTypeAdapter(String.class, STRING);   //所有String类型null替换为字符串“”        gsonBulder.registerTypeAdapter(int.class, INTEGER); //int类型对float做兼容        //通过反射获取instanceCreators属性        try {            Class builder = (Class) gsonBulder.getClass();            Field f = builder.getDeclaredField("instanceCreators");            f.setAccessible(true);            Map
> val = (Map
>) f.get(gsonBulder);//得到此属性的值 //注册数组的处理器 gsonBulder.registerTypeAdapterFactory(new CollectionTypeAdapterFactory(new ConstructorConstructor(val))); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } gson = gsonBulder.create(); }复制代码

定制化解析

全局替换的方式比较粗暴,对于复杂工程可能会引起预料不到的问题,可以结合注解等方式,对指定的元素进行特殊处理。

设计一个NonNullField字段,注解到自定义类的成员变量上,可以确保解析时该字段不会为null,会使用默认的实例来代替。

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface NonNullField {    Class
value() default NonNullFieldConstructor.class;}复制代码

NonNullField注解通过NonNullFieldFactory实现如下。此Factory在创建TypeAdapter时,先搜索Class和父类中包含NonNullField注解的成员变量:

  1. 如果没找到,则返回null,由其他Factory创建TypeAdapter;
  2. 如果找到了,则在DelegateAdapter外包裹一层,DelegateAdapter解析完成后调用replaceNonNullFields方法,将NonNullField的null值替换为默认实例。
public class NonNullFieldFactory implements TypeAdapterFactory {    private static final String ANNOTATION_NAME = NonNullField.class.getSimpleName();    /**     * 保存Type及其对应的NonNullField     */    private static final Map
> fieldMap = new ConcurrentHashMap<>(); /** * InstanceCreator缓存 */ private static final Map
, InstanceCreator> creatorCache = new ConcurrentHashMap<>(); @Override public
TypeAdapter
create(Gson gson, TypeToken
typeToken) { List
fields = findMatchedFields(typeToken); final Type type = typeToken.getType(); // 如果找到了,则包裹一层Adapter if (fields != null && !fields.isEmpty()) { fieldMap.put(type, fields); final TypeAdapter
delegate = gson.getDelegateAdapter(this, typeToken); log("create wrapper adapter, type = %s, find %d fields, delegate = %s", typeToken, fields.size(), delegate); return new TypeAdapter
() { @Override public void write(JsonWriter out, T value) throws IOException { delegate.write(out, value); } @Override public T read(JsonReader in) throws IOException { T t = delegate.read(in); log(" finish read, data = %s, type = %s, delegate = %s", t, type, delegate); replaceNonNullFields(t, typeToken); return t; } }; } return null; } private static void log(String msg, Object... args) { L.d(GsonUtils.TAG, "[NonNullFieldFactory] " + msg, args); } /** * 是否需要搜索Type中的Field */ @SuppressWarnings("RedundantIfStatement") private static boolean shouldSearch(Class clazz) { // 跳过不需要搜索的类 if (clazz == null || clazz == Object.class || clazz.isPrimitive() || clazz.isEnum() || clazz.isArray()) { log("skip search class %s", clazz); return false; } // 跳过Java和Android系统中的类 String packageName = clazz.getPackage().getName(); if (packageName.startsWith("java") || packageName.startsWith("android")) { log("skip search class %s by package", clazz); return false; } // 只匹配特定的类、跳过其他第三方库的类…… return true; } /** * 找到某个Type中的NonNullField,包括继承的 */ private static List
findMatchedFields(TypeToken typeToken) { List
list = null; Class raw = typeToken.getRawType(); while (shouldSearch(raw)) { Field[] fields = raw.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (field.getAnnotation(NonNullField.class) != null) { if (list == null) { list = new ArrayList<>(); } list.add(field); } } // 解析父类 typeToken = TypeToken.get($Gson$Types.resolve(typeToken.getType(), typeToken.getRawType(), raw.getGenericSuperclass())); raw = typeToken.getRawType(); } return list == null ? Collections.EMPTY_LIST : list; } /** * 解析Field的Type,处理泛型参数 * * @param typeToken Field所在类的Type * @param field 要解析的Field */ private static Type resolveFieldType(TypeToken typeToken, Field field) { return $Gson$Types.resolve(typeToken.getType(), typeToken.getRawType(), field.getGenericType()); } /** * 填充对象中的NonNullField */ private static void replaceNonNullFields(Object o, TypeToken typeToken) { if (o == null) { return; } // 对于嵌套注解的情况(NonNullField对应类型中又有NonNullField), // 由于Gson会先解析内部数据,其TypeAdapter已经创建,此处map可以取到值 List
fields = fieldMap.get(typeToken.getType()); if (fields == null || fields.isEmpty()) { return; } for (Field field : fields) { try { Object fieldValue = field.get(o); if (fieldValue == null) { Object value = constructField(field, resolveFieldType(typeToken, field)); if (value == null) { throw new RuntimeException(String.format("Create field %s for type %s failure", field.getName(), typeToken.getType())); } field.set(o, value); log(" --> set field '%s.%s' to '%s'", typeToken.getType().getTypeName(), field.getName(), value); } } catch (IllegalArgumentException | IllegalAccessException e) { L.e(e); } } } private static Object constructField(Field field, Type type) { NonNullField annotation = field.getAnnotation(NonNullField.class); Class
creatorClass = annotation.value(); InstanceCreator creator = getCreator(creatorClass); Object instance = creator.createInstance(type); replaceNonNullFields(instance, TypeToken.get(type)); return instance; } private static synchronized InstanceCreator getCreator(Class
creatorClass) { InstanceCreator creator = creatorCache.get(creatorClass); if (creator == null) { try { creator = creatorClass.newInstance(); creatorCache.put(creatorClass, creator); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException("InstanceCreator " + creatorClass + " create failure", e); } } return creator; }}复制代码

在replaceNonNullFields方法中,调用InstanceCreator创建实例,然后通过反射设置给Java对象。创建默认实例时,还会递归替换默认实例中嵌套的NonNullField,从而支持嵌套class。

InstanceCreator可由NonNullField注解指定,默认值为NonNullFieldConstructor。NonNullFieldConstructor中先判断如果是基本类型或数组,则直接创建,否则调用Gson内部的ConstructorConstructor工具类创建实例。

public class NonNullFieldConstructor implements InstanceCreator {    /**     * 保存基本类型及其默认值。基本类型默认值的内容不能被修改,因此可以重复利用,赋值给多个Field。     */    private static final Map
basicMap = new HashMap<>(); /** * Gson的Constructor */ private static final ConstructorConstructor constructor = new ConstructorConstructor(new HashMap<>()); static { basicMap.put(Boolean.class, false); basicMap.put(Byte.class, (byte) 0); basicMap.put(Character.class, (char) 0); basicMap.put(Short.class, (short) 0); basicMap.put(Integer.class, 0); basicMap.put(Long.class, 0L); basicMap.put(Float.class, 0F); basicMap.put(Double.class, (double) 0); basicMap.put(String.class, ""); } @Override public Object createInstance(Type type) { if (type instanceof Class) { Object o = basicMap.get(type); if (o != null) { // Integer.class return o; } else if (((Class) type).isArray()) { // String[].class return Array.newInstance($Gson$Types.getRawType(((Class) type).getComponentType()), 0); } } else if (type instanceof GenericArrayType) { // String[] return Array.newInstance($Gson$Types.getRawType(((GenericArrayType) type).getGenericComponentType()), 0); } // 其他类型使用constructor创建 TypeToken
typeToken = TypeToken.get(type); return constructor.get(typeToken).construct(); }}复制代码

注册:

Gson gson = new GsonBuilder().registerTypeAdapterFactory(new NonNullFieldFactory()).create();复制代码

解析后校验数据

可以设计一个IDataValidateAction接口定义如下。自动解析完成后,如果对象实现了这个接口,Gson就会调用isDataValid校验数据,如果数据无效,则直接过滤掉这个对象,返回null。

public interface IDataValidateAction {    boolean isDataValid();}复制代码
public static class User implements IAfterDeserializeAction {    private long id;    private String name;    public long getId() {        return id;    }    public String getName() {        return name;    }    @Override    public void doAfterDeserialize() {        if (name == null || name.length() == 0) {            name = "匿名";        }    }}public static class ValidUser extends User implements IDataValidateAction {    @Override    public boolean isDataValid() {        // 如果id为0或负值,说明接口异常,视为无效数据,丢弃不用        return getId() > 0;    }}复制代码

只需要给Gson注册一个DeserializeActionAdapterFactory即可。这个Factory会判断如果Type实现了DeserializeAction相关接口,则在DelegateAdapter外包裹一层进行相应的处理;否则直接返回DelegateAdapter。

public class DeserializeActionFactory implements TypeAdapterFactory {    public 
TypeAdapter
create(Gson gson, final TypeToken
type) { // 获取其他低优先级Factory创建的DelegateAdapter final TypeAdapter
delegate = gson.getDelegateAdapter(this, type); // 如果type实现了DeserializeAction,则返回包裹后的TypeAdapter if (shouldWrap(type.getRawType())) { L.d(GsonUtils.TAG, "[DeserializeAction] create return new adapter, type = %s, delegate = %s", type, delegate); return new TypeAdapter
() { public void write(JsonWriter out, T value) throws IOException { delegate.write(out, value); } public T read(JsonReader in) throws IOException { T t = delegate.read(in); L.d(GsonUtils.TAG, "[DeserializeAction] finish read, data = %s, type = %s, delegate = %s", t, type, delegate); if (isInvalidData(t)) { return null; } doAfterDeserialize(t); return t; } }; } else { L.d(GsonUtils.TAG, "[DeserializeAction] create return delegate, type = %s, delegate = %s", type, delegate); return delegate; } } public static boolean isInvalidData(Object t) { if (t instanceof IDataValidateAction) { if (!((IDataValidateAction) t).isDataValid()) { L.d(GsonUtils.TAG, "[DeserializeAction] --> data is invalid"); return true; } } return false; } public static
void doAfterDeserialize(Object t) { if (t instanceof IAfterDeserializeAction) { ((IAfterDeserializeAction) t).doAfterDeserialize(); L.d(GsonUtils.TAG, "[DeserializeAction] --> processed data = %s", t); } } private boolean shouldWrap(Class clazz) { return IAfterDeserializeAction.class.isAssignableFrom(clazz) || IDataValidateAction.class.isAssignableFrom(clazz); }}复制代码

注册:

Gson gson = new GsonBuilder().registerTypeAdapterFactory(new DeserializeActionFactory()).create();复制代码

转载地址:http://pnbsl.baihongyu.com/

你可能感兴趣的文章
Microsoft Dynamics CRM 2013 配置之 添加配置 域证书服务器 和 ADFS
查看>>
your windows password does not match your Notes password
查看>>
TCP: time wait bucket table overflow解决方法
查看>>
CSS样式中设置table的cellspacing属性
查看>>
The method getTextContent() is undefined for the type Node
查看>>
iPhone动画属性详解
查看>>
fatal error: 'openssl/err.h' file not found
查看>>
zabbix实现 SAS 6/iR 型号 Raid信息监控
查看>>
RHEL Centos7 Yum网络源与光盘源设置
查看>>
一条sql语句实现一维表生成二维表格
查看>>
我的友情链接
查看>>
从“赢”字诠释解读成功的必备要素(一)
查看>>
面试心得
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
2018-08-16期 HBase全分布模式集群及HMaster HA安装部署
查看>>
docker中的容器互联-linking系统
查看>>
Linux学习之CentOS(二十一)--Linux系统启动详解
查看>>
escape()、encodeURI()、encodeURIComponent()区别详解
查看>>
AgileEAS.NET5.0-界面设计器-使用说明书(上)
查看>>