當前項目解析json用的工具是google的gson,緣由嘛,由於有GsonFormat插件,能夠直接把服務端傳回的json字符串轉成Bean對象。不過在實際使用中出現瞭如下兩個問題:html
針對上述問題,均可以經過自定義TypeAdapter解決。java
閱讀過Gson的源碼後發現,Gson的數據解析都是委託到各個TypeAdapter內進行處理的。在Gson的構造函數內會預先加載一部分TypeAdapter,包含String、int、long、double等類型,都存放在factories中,以下:git
Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingPolicy, final Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls, boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe, boolean prettyPrinting, boolean serializeSpecialFloatingPointValues, LongSerializationPolicy longSerializationPolicy, List<TypeAdapterFactory> typeAdapterFactories) { this.constructorConstructor = new ConstructorConstructor(instanceCreators); this.serializeNulls = serializeNulls; this.generateNonExecutableJson = generateNonExecutableGson; this.htmlSafe = htmlSafe; this.prettyPrinting = prettyPrinting; List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>(); // built-in type adapters that cannot be overridden factories.add(TypeAdapters.JSON_ELEMENT_FACTORY); factories.add(ObjectTypeAdapter.FACTORY); // the excluder must precede all adapters that handle user-defined types factories.add(excluder); // user's type adapters factories.addAll(typeAdapterFactories); // type adapters for basic platform types factories.add(TypeAdapters.STRING_FACTORY); factories.add(TypeAdapters.INTEGER_FACTORY); factories.add(TypeAdapters.BOOLEAN_FACTORY); factories.add(TypeAdapters.BYTE_FACTORY); factories.add(TypeAdapters.SHORT_FACTORY); factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter(longSerializationPolicy))); factories.add(TypeAdapters.newFactory(double.class, Double.class, doubleAdapter(serializeSpecialFloatingPointValues))); factories.add(TypeAdapters.newFactory(float.class, Float.class, floatAdapter(serializeSpecialFloatingPointValues))); factories.add(TypeAdapters.NUMBER_FACTORY); factories.add(TypeAdapters.CHARACTER_FACTORY); factories.add(TypeAdapters.STRING_BUILDER_FACTORY); factories.add(TypeAdapters.STRING_BUFFER_FACTORY); factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL)); factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER)); factories.add(TypeAdapters.URL_FACTORY); factories.add(TypeAdapters.URI_FACTORY); factories.add(TypeAdapters.UUID_FACTORY); factories.add(TypeAdapters.LOCALE_FACTORY); factories.add(TypeAdapters.INET_ADDRESS_FACTORY); factories.add(TypeAdapters.BIT_SET_FACTORY); factories.add(DateTypeAdapter.FACTORY); factories.add(TypeAdapters.CALENDAR_FACTORY); factories.add(TimeTypeAdapter.FACTORY); factories.add(SqlDateTypeAdapter.FACTORY); factories.add(TypeAdapters.TIMESTAMP_FACTORY); factories.add(ArrayTypeAdapter.FACTORY); factories.add(TypeAdapters.ENUM_FACTORY); factories.add(TypeAdapters.CLASS_FACTORY); // type adapters for composite and user-defined types factories.add(new CollectionTypeAdapterFactory(constructorConstructor)); factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization)); factories.add(new ReflectiveTypeAdapterFactory( constructorConstructor, fieldNamingPolicy, excluder)); this.factories = Collections.unmodifiableList(factories); }
咱們能夠自定義TypeAdapter,將其放入facotries中,而且gson在解析json時使用對應的TypeAdapter來的,而咱們手動添加的TypeAdapter會優先於預設的TypeAdapter被使用。有興趣的能夠看看源碼,仍是比較簡單的。
github
爲了解決問題1,咱們先定義一個StringAdapter,代碼以下:json
/** * 自定義TypeAdapter ,null對象將被解析成空字符串 */ 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(); } } };
這樣在讀取到null節點時,就自動變成返回空字符串了。數組
接下去定義一個Int類型的TypeAdapter:app
/** * 自定義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是不可重寫的,只好拷貝出來,從新寫了個類,以下ide
import com.google.gson.Gson; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; import com.google.gson.internal.$Gson$Types; import com.google.gson.internal.ConstructorConstructor; import com.google.gson.internal.ObjectConstructor; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.lang.reflect.Type; import java.util.Collection; /** * 自定義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(); } } }
注意上面的TypeAdapterRuntimeTypeWrapper類不是public的,因此也得拷貝出來寫一個到本地。函數
接下去是使用部分:工具
import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.InstanceCreator; import com.google.gson.JsonElement; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import com.google.gson.JsonSyntaxException; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; import com.google.gson.internal.ConstructorConstructor; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; /** * Created by linjizong on 15/7/20. */ public class GsonUtils { public static Gson gson; /** * 自定義TypeAdapter ,null對象將被解析成空字符串 */ 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(); } } }; /** * 自定義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(); return (int) i; } catch (NumberFormatException e) { throw new JsonSyntaxException(e); } } @Override public void write(JsonWriter out, Number value) throws IOException { out.value(value); } }; 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(); } /** * Json字符串 轉爲指定對象 * * @param json json字符串 * @param type 對象類型 * @param <T> 對象類型 * @return * @throws JsonSyntaxException */ public static <T> T toBean(String json, Class<T> type) throws JsonSyntaxException { T obj = gson.fromJson(json, type); return obj; } }
經過GsonBuilder的registerTypeAdapter方法能夠直接註冊TypeAdapter。而CollectionTypeAdapterFactory方法須要使用到GsonBuidler的instanceCreators字段,只好經過反射來獲取了。接下去只要使用GsonUtils.toBean()就好了。