在數據Model實現類中過濾掉null值。例如設計一個List,只有非null值才能被添加進去,取出Item時就不須要判空了。java
能夠封裝一個ItemNonNullList,在ArrayList外面包一層,並特殊處理add/addAll相關方法,保證爲null的Item不會被添加進去。不須要自定義TypeAdapter修改Gson解析過程。android
public class ItemNonNullList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
@NotNull
private final ArrayList<E> mList;
public ItemNonNullList() {
mList = new ArrayList<>();
}
@SuppressWarnings("unchecked")
private ItemNonNullList(@NotNull ItemNonNullList<E> other) {
mList = (ArrayList<E>) 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<E> iterator() {
return mList.iterator();
}
@NotNull
@Override
public Object[] toArray() {
return mList.toArray();
}
@NotNull
@Override
public <T> 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<? extends E> 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<? extends E> c) {
ArrayList<E> 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<E> listIterator() {
return mList.listIterator();
}
@NotNull
@Override
public ListIterator<E> listIterator(int index) {
return mList.listIterator(index);
}
@NotNull
@Override
public List<E> subList(int fromIndex, int toIndex) {
return mList.subList(fromIndex, toIndex);
}
@SuppressWarnings("MethodDoesntCallSuperMethod")
@Override
public ItemNonNullList<E> clone() {
return new ItemNonNullList<>(this);
}
}
複製代碼
對於特定類型,全局替換Gson解析過程。例如String類型null解析爲空串"";數組、List解析爲空數組、空List而不是null等。json
以String爲例,能夠把TypeAdapters.STRING複製出來,並修改其中代碼以下,將null解析爲空字符串,而後註冊到Gson中,覆蓋String默認的TypeAdapter。數組
修改前:緩存
public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
@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);
}
};
複製代碼
修改後:bash
public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
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、數組等類型的免判空。app
public static final TypeAdapter<Number> INTEGER = new TypeAdapter<Number>() {
@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<Number> INTEGER = new TypeAdapter<Number>() {
@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的,因此也得拷貝出來寫一個到本地。dom
/**
* 自定義CollectionTypeAdapterFactory,使json內的數組爲null時,返回空數組而不是null對象
*/
public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
private final ConstructorConstructor constructorConstructor;
public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
this.constructorConstructor = constructorConstructor;
}
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
Class<? super T> 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<T> constructor = constructorConstructor.get(typeToken);
@SuppressWarnings({"unchecked", "rawtypes"}) // create() doesn't define a type parameter TypeAdapter<T> result = new Adapter(gson, elementType, elementTypeAdapter, constructor); return result; } private static final class Adapter<E> extends TypeAdapter<Collection<E>> { private final TypeAdapter<E> elementTypeAdapter; private final ObjectConstructor<? extends Collection<E>> constructor; public Adapter(Gson context, Type elementType, TypeAdapter<E> elementTypeAdapter, ObjectConstructor<? extends Collection<E>> constructor) { this.elementTypeAdapter = new TypeAdapterRuntimeTypeWrapper<E>(context, elementTypeAdapter, elementType); this.constructor = constructor; } public Collection<E> read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); //這裏作了修改,原先是返回null,改成返回空數組 return constructor.construct(); } Collection<E> 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<E> collection) throws IOException { if (collection == null) { out.nullValue(); return; } out.beginArray(); for (E element : collection) { elementTypeAdapter.write(out, element); } out.endArray(); } } } 複製代碼
進行註冊:ide
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<Type, InstanceCreator<?>> val = (Map<Type, InstanceCreator<?>>) 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<? extends InstanceCreator> value() default NonNullFieldConstructor.class;
}
複製代碼
NonNullField註解經過NonNullFieldFactory實現以下。此Factory在建立TypeAdapter時,先搜索Class和父類中包含NonNullField註解的成員變量:
public class NonNullFieldFactory implements TypeAdapterFactory {
private static final String ANNOTATION_NAME = NonNullField.class.getSimpleName();
/**
* 保存Type及其對應的NonNullField
*/
private static final Map<Type, List<Field>> fieldMap = new ConcurrentHashMap<>();
/**
* InstanceCreator緩存
*/
private static final Map<Class<? extends InstanceCreator>, InstanceCreator> creatorCache = new ConcurrentHashMap<>();
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
List<Field> fields = findMatchedFields(typeToken);
final Type type = typeToken.getType();
// 若是找到了,則包裹一層Adapter
if (fields != null && !fields.isEmpty()) {
fieldMap.put(type, fields);
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, typeToken);
log("create wrapper adapter, type = %s, find %d fields, delegate = %s", typeToken, fields.size(), delegate);
return new TypeAdapter<T>() {
@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<Field> findMatchedFields(TypeToken typeToken) {
List<Field> 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<Field> 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<? extends InstanceCreator> 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<? extends InstanceCreator> 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<Object> {
/**
* 保存基本類型及其默認值。基本類型默認值的內容不能被修改,所以能夠重複利用,賦值給多個Field。
*/
private static final Map<Class, Object> 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 <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
// 獲取其餘低優先級Factory建立的DelegateAdapter
final TypeAdapter<T> 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<T>() {
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 <T> 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();
複製代碼