微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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();
即可

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


AJAX是一种基于JavaScript和XML的技术,能够使网页实现异步交互,节省带宽和时间,提高用户体验。在使用AJAX时,需要通过解析JSON格式的数据,来获取所需要的数据。
在网页开发中,我们常常需要通过Ajax从后端获取数据并在页面中展示出来。其中,JSON是一种常用的数据格式。那么,在使用Ajax获取JSON数据后,如何将数据取出来呢?
在前端开发中,经常需要循环JSON对象数组进行数据操作。使用AJAX技术可以在不刷新页面的情况下异步获取数据。那么我们该如何循环JSON对象数组呢?下面我们通过一段代码来进行讲解。
AJAX(Asynchronous JavaScript and XML)是一种用于创建 Web 应用程序的技术,它使用 JavaScript 和 XML(或 JSON)来在后台异步传输数据。
AJAX技术被广泛应用于现代Web开发,它可以在无需重新加载页面的情况下,向服务器发出请求并更新页面,实现了异步更新的效果。而传递JSON数据是AJAX中比较常见的一种方法,下面是如何使用AJAX传递JSON数据的详细介绍。
Ajax是一种通过JavaScript和HTTP请求交互的技术,可以实现无需刷新页面的异步数据交互。在处理数据时,常常需要删除一些已存在的数据。本文将介绍如何使用Ajax删除JSON数据库中的数据。
在使用Ajax时,我们经常需要将数据格式化为JSON格式。JSON是一种轻量级数据交换格式,它以键值对的形式来表达数据。
AJAX是一种支持异步请求的技术,它可以让前端页面不用刷新就能向后台请求数据,并异步地展示给用户,提高了用户的体验感。其中,使用JSON格式化数据可以帮助我们更方便快捷地处理返回的数据。
AJAX是一种前端技术,可以通过异步请求来获取数据,并在页面上更新它们。JSON是一种轻量级的数据交换格式,因为它易于读取和编写,因此在Web应用程序中被广泛使用。AJAX传送JSON数据是一种常见的技术,可以让Web应用
在前端开发中,ajax是很常见的技术,它可以在不刷新整个页面的情况下请求服务器数据和更新部分页面。而当需要遍历多个json文件时,可以使用ajax循环遍历来实现。