Gson uses TypeAdapter or Json Deserializer to convert data from an error list to an empty list

前端 未结 1 1189
花落未央
花落未央 2021-01-27 03:24

Let\'s start from example:

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

{
   \"code\":200,
   \"msg\":\         


        
相关标签:
1条回答
  • 2021-01-27 03:59

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

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

    How to use

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

    prints:

    Result{code=200, msg='success', data=[Province{id=1, name='Beijing', cities=[]}, Province{id=2, name='Guangdong', cities=[City{id=1, name='Guangzhou'}]}]}
    
    0 讨论(0)
提交回复
热议问题