Gson使用TypeAdapter或Json Deserializer将数据从错误列表转换为空列表 [英] Gson uses TypeAdapter or Json Deserializer to convert data from an error list to an empty list

查看:109
本文介绍了Gson使用TypeAdapter或Json Deserializer将数据从错误列表转换为空列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们从示例开始:

如果数据正确,则应该为(Beijing cities为空)

If the data is correct, it should be ( the Beijing cities is empty )

{
   "code":200,
   "msg":"success",
   "data":[
      {
         "id":1,
         "name":"Beijing",
         "cities":[]
      },
      {
         "id":2,
         "name":"Guangdong",
         "cities":[
            {
               "id":1,
               "name":"Guangzhou"
            }
         ]
      }
   ]
}

现在我得到了错误的数据. (Beijing cities为null)

Now I got a wrong data. ( the Beijing cities is null )

{
   "code":200,
   "msg":"success",
   "data":[
      {
         "id":1,
         "name":"Beijing",
         "cities":null
      },
      {
         "id":2,
         "name":"Guangdong",
         "cities":[
            {
               "id":1,
               "name":"Guangzhou"
            }
         ]
      }
   ]
}

我正在使用Retrofit2 ResponseBodyConverter实体类:

public class Result<T> {
    private int code;
    private String msg;
    private T data;

    // getters, setters
}

public class Province {
    private int id;
    private String name;
    private List<City> cities;

}

public class City {
    private int id;
    private String name;

}

反序列化后获得的数据如下:

The data obtained after deserialization is like this:

但是我需要的数据是这样的:

but the data I need is like this:

为了具有更好的容错能力,当列出数据时,我想自己处理.
首先,我尝试使用JsonDeserializer

In order to have better fault tolerance, when the data is list, I want to process it by myself.
First of all,I tried to use JsonDeserializer

Gson gson = new GsonBuilder()
              .serializeNulls()
              .registerTypeHierarchyAdapter(List.class, new GsonListAdapter())
              .create();

static class GsonListAdapter implements JsonDeserializer<List<?>> {
    @Override
    public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        if (json.isJsonArray()) {
            JsonArray array = json.getAsJsonArray();
            Type itemType = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
            List list = new ArrayList<>();
            for (int i = 0; i < array.size(); i++) {
                JsonElement element = array.get(i);
                Object item = context.deserialize(element, itemType);
                list.add(item);
            }
            return list;
        } else {
            return Collections.EMPTY_LIST;
        }
    }
}

JsonDeserializer在数据为""{}[]时有效,但datanull时将不起作用.

JsonDeserializer is valid when the data is "", {}, and [],but data is null, it will not work.

然后我尝试使用TypeAdapter

static class GsonListAdapter extends TypeAdapter<List<?>> {

    @Override
    public void write(JsonWriter out, List<?> value) throws IOException {
        out.value(String.valueOf(value));
    }

    @Override
    public List<?> read(JsonReader reader) throws IOException {
        if (reader.peek() != JsonToken.BEGIN_ARRAY) {
            reader.skipValue();
            return Collections.EMPTY_LIST;
        }
        return new Gson().fromJson(reader, new TypeToken<List<?>>() {}.getType());
    }
}

这样,无论data是什么,它都可以正常工作.我们知道使用TypeToken<List<?>>会给我们LinkedHashMap,因此,尽管TypeAdapter可以正常工作,但我不能知道如何将JsonReader转换为List <?>.

In this way, no matter what the data is, it can work properly.We know that using TypeToken<List<?>> will give us the LinkedHashMap,So although TypeAdapter can work properly, but I don't know how to convert JsonReader to the List <?>.

所以我想知道是否还有其他方法可以处理错误的列表数据?或将JsonReader转换为我想要的List <?> data.

So I wonder if there are other ways that I can handle the wrong list data? Or convert JsonReader to the List <?> data I want.

推荐答案

我在Gson源代码中找到了CollectionTypeAdapterFactory.我试图对其进行修改,它已经过测试,非常有用.

I found the CollectionTypeAdapterFactory in Gson source code.I tried to modify it,it has been tested and it is useful.

public 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();
                //In the source code is return null, I changed to return an empty collection
                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受保护,我们必须进行复制.

In the source code the TypeAdapterRuntimeTypeWrapper is protected,We must make a copy.

  public class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
      private final Gson context;
      private final TypeAdapter<T> delegate;
      private final Type type;

      TypeAdapterRuntimeTypeWrapper(Gson context, TypeAdapter<T> delegate, Type type) {
          this.context = context;
          this.delegate = delegate;
          this.type = type;
      }

      @Override
      public T read(JsonReader in) throws IOException {
          return delegate.read(in);
      }

      @SuppressWarnings({"rawtypes", "unchecked"})
      @Override
      public void write(JsonWriter out, T value) throws IOException {
          TypeAdapter chosen = delegate;
          Type runtimeType = getRuntimeTypeIfMoreSpecific(type, value);
          if (runtimeType != type) {
              TypeAdapter runtimeTypeAdapter = context.getAdapter(TypeToken.get(runtimeType));
              if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) {
                  // The user registered a type adapter for the runtime type, so we will use that
                  chosen = runtimeTypeAdapter;
              } else if (!(delegate instanceof ReflectiveTypeAdapterFactory.Adapter)) {
                  // The user registered a type adapter for Base class, so we prefer it over the
                  // reflective type adapter for the runtime type
                  chosen = delegate;
              } else {
                  // Use the type adapter for runtime type
                  chosen = runtimeTypeAdapter;
              }
          }
          chosen.write(out, value);
      }

      private Type getRuntimeTypeIfMoreSpecific(Type type, Object value) {
          if (value != null
                  && (type == Object.class || type instanceof TypeVariable<?> || type instanceof Class<?>)) {
              type = value.getClass();
          }
          return type;
      }
  }

使用方法

Gson gson = new GsonBuilder().serializeNulls()
           .registerTypeAdapterFactory(
             new CollectionTypeAdapterFactory(new ConstructorConstructor(new HashMap<>()))
             )
           .create();

Result<List<Province>> result = gson.fromJson(jsonStr, new TypeToken<Result<List<Province>>>() {}.getType());

打印:

Result{code=200, msg='success', data=[Province{id=1, name='Beijing', cities=[]}, Province{id=2, name='Guangdong', cities=[City{id=1, name='Guangzhou'}]}]}

这篇关于Gson使用TypeAdapter或Json Deserializer将数据从错误列表转换为空列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆