Gson将JsonObject转JavaBean整条崩溃状况解决

昨天被提了一个需求,最近后台传来的json数据,部分类型和javabean所需要的类型有出入,导致app整个崩溃,希望对网络模块能对转换过程进行控制

虽然这是一个后台的锅,但是被甩给网络模块并甩给了我。表示无奈。。。我只是来实习的啊

为了解决这个问题,需要更深入的了解Gson,所以先用retrofit写了一个demo

retrofit = new Retrofit.Builder()
        .baseUrl("https://api.douban.com/v2/")
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build();
service= retrofit.create(RetrofitService.class);

不难看出,retrofit在此处添加了一个转换工厂,addConverterFactory(GsonConverterFactory.create(gson)),转换任务应当都交给了此处的GsonConverterFactory

所以跟进查看GsonConverterFactory

@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
    Retrofit retrofit) {
  TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
  return new GsonResponseBodyConverter<>(gson, adapter);
}

@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
    Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
  TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
  return new GsonRequestBodyConverter<>(gson, adapter);
}
然后在这个工厂中,一眼就可以看出最重要的就是这个请求和收到的回复的转换是通过上面两个函数

我们需要处理的是收到的回复,所以,主要看responseBodyConverter方法

第一句获得了一个TypeAdapter,稍微找点博客就可以知道,这里的TypeAdapter就是用来处理中间层,推荐博文点击打开链接

这一句的大意应该是首先获得了传入的JavaBean类的typeToken,然后根据TypeToken,gson对象动态生成了一个对应的TypeAdapter

虽然懂了,但是对于这种动态生成的过程,表示还是非常的恐惧,本来想求助一下,并没有找到相关的资料

所以只能自己接着看获取getAdapter的方法

@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
  TypeAdapter<?> cached = typeTokenCache.get(type);
  if (cached != null) {
    return (TypeAdapter<T>) cached;
  }

  Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
  boolean requiresThreadLocalCleanup = false;
  if (threadCalls == null) {
    threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
    calls.set(threadCalls);
    requiresThreadLocalCleanup = true;
  }

  // the key and value type parameters always agree
  FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
  if (ongoingCall != null) {
    return ongoingCall;
  }

  try {
    FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
    threadCalls.put(type, call);

    for (TypeAdapterFactory factory : factories) {
      TypeAdapter<T> candidate = factory.create(this, type);
      if (candidate != null) {
        call.setDelegate(candidate);
        typeTokenCache.put(type, candidate);
        return candidate;
      }
    }
    throw new IllegalArgumentException("GSON cannot handle " + type);
  } finally {
    threadCalls.remove(type);

    if (requiresThreadLocalCleanup) {
      calls.remove();
    }
  }
}
上面就是获取typeAdapter的方法,可以看到最重要的下面这句

for (TypeAdapterFactory factory : factories) {
  TypeAdapter<T> candidate = factory.create(this, type);
  if (candidate != null) {
    call.setDelegate(candidate);
    typeTokenCache.put(type, candidate);
    return candidate;
  }
}

可以看出,该方法便利typeAdpterFactory集合,生成了TypeAdapter,那么这个factories到底是什么

// 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);
TypeAdapter<Number> longAdapter = longAdapter(longSerializationPolicy);
factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter));
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.ATOMIC_INTEGER_FACTORY);
factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY);
factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter)));
factories.add(TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter)));
factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_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.CURRENCY_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.CLASS_FACTORY);

// type adapters for composite and user-defined types
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
factories.add(new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor));
factories.add(TypeAdapters.ENUM_FACTORY);
factories.add(new ReflectiveTypeAdapterFactory(
    constructorConstructor, fieldNamingPolicy, excluder));
原来在Gson生成时,就自定义添加了大量的factory,具体找一个String类型的Factory看一下

public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING);

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);
  }
};

这里我猜想是通过TypeAdapter返回的类型,递归的实现对应类的TypeAdapter。而且在这个typeAdapter中是可以对输入的Typetoken进行判断的,所以如果我能够干涉这个factory集合,就可以实现所需要的功能。

索性,再Gson builder中,作者预留了这个接口

public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) {
  factories.add(factory);
  return this;
}
而且,再GsonBuilder生成Gson的过程中,是先把自定义的TypeAdapterFactory添加进去的,而遍历TypeAdapterFactory集合的时候,一旦有了符合的对象,就会返回TypeAdapter的。这就意味着,自定义的TypeAdapterFactory是有更高的优先级。所以直接添加自定义的TypeAdapterFactory就可以实现需求

例如,我需要对String类型预先判断,那么就可以这么做

1.先自定义一个TypeAdapters和对应的TypeAdapterFactory

public class TypeAdapters {
    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());
            }
            if(peek!=JsonToken.STRING){
                in.skipValue();
                return "";
            }
            return in.nextString();
        }
        @Override
        public void write(JsonWriter out, String value) throws IOException {
            out.value(value);
        }
    };
    public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING);
}

然后在构造Gson中

GsonBuilder gsonBuild=new GsonBuilder();
Gson gson=gsonBuild.registerTypeAdapterFactory(TypeAdapters.STRING_FACTORY).create();
即可

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值