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

前端 未结 1 1187
花落未央
花落未央 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  TypeAdapter create(Gson gson, TypeToken typeToken) {
            Type type = typeToken.getType();
    
            Class 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 constructor = constructorConstructor.get(typeToken);
    
            @SuppressWarnings({"unchecked", "rawtypes"}) // create() doesn't define a type parameter
                    TypeAdapter result = new Adapter(gson, elementType, elementTypeAdapter, constructor);
            return result;
        }
    
        private static final class Adapter extends TypeAdapter> {
            private final TypeAdapter elementTypeAdapter;
            private final ObjectConstructor> constructor;
    
            public Adapter(Gson context, Type elementType,
                           TypeAdapter elementTypeAdapter,
                           ObjectConstructor> constructor) {
                this.elementTypeAdapter =
                        new TypeAdapterRuntimeTypeWrapper(context, elementTypeAdapter, elementType);
                this.constructor = constructor;
            }
    
            public Collection 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 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 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 extends TypeAdapter {
          private final Gson context;
          private final TypeAdapter delegate;
          private final Type type;
    
          TypeAdapterRuntimeTypeWrapper(Gson context, TypeAdapter 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> result = gson.fromJson(jsonStr, new TypeToken>>() {}.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)
提交回复
热议问题