【问题标题】:How to set super class properties when parsing JSON with gson使用gson解析JSON时如何设置超类属性
【发布时间】:2017-04-06 15:22:59
【问题描述】:

解析JSON数组时如何设置超类“LookUp”属性id、name 国家/地区:

[ {"ID":5, "CountryNameEN":"UK" }, {"ID":6, "CountryNameEN":"USA" }  ]

例如,当我使用 Retrofit 2 调用 get_lookups_countries() API 并使用谷歌 Gson 库解析响应时,我想将超类实例成员 id 和名称设置为派生类“Country”的相同值

 @GET(Constants.LookUps.GET_COUNTRIES) Call<List<Country>> get_lookups_countries();
Gson gson = new GsonBuilder()
           .setLenient()
           .registerTypeAdapter(LookUp.class,new LookupsDeserializer())
           .create();

   HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
   logging.setLevel(HttpLoggingInterceptor.Level.BODY);
   OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder();

   Retrofit retrofit = new Retrofit.Builder()
           .baseUrl(BASE_URL)
           .client(okHttpClient.build())
           .addConverterFactory(GsonConverterFactory.create(gson))
           .build();
   return retrofit.create(APIEndpointVatTax.class);
public class LookUp {
    int id;
    String name;
}


public class Country extends LookUp {

        @SerializedName("ID")
        @Expose
        private Integer iD;

        @SerializedName("CountryNameEN")
        @Expose
        private String countryNameEN;
}

【问题讨论】:

  • Lookup id 和 Country iD 不同吗?
  • 不,相同的值
  • 那为什么需要超类呢?

标签: java android gson retrofit


【解决方案1】:

您的 JSON 映射似乎存在一些问题:您正在尝试将超类字段绑定到子类字段,但是这对您来说接口可能是更好的选择,因为您的意图只是 向反序列化对象询问其 id 和名称。

我会这样做:

interface LookUp {

    int getId();

    String getName();

}
final class CountryByInterface
        implements LookUp {

    @SerializedName("ID")
    private final Integer id = null;

    @SerializedName("CountryNameEN")
    private final String name = null;

    @Override
    public int getId() {
        return id;
    }

    @Override
    public String getName() {
        return name;
    }

}

因此可以轻松使用(Java 8 仅用于演示目的):

final Gson gson = new Gson();
final Type countryListType = new TypeToken<List<CountryByInterface>>() {
}.getType();
try ( final Reader reader = getPackageResourceReader(Q43247712.class, "countries.json") ) {
    gson.<List<CountryByInterface>>fromJson(reader, countryListType)
            .stream()
            .map(c -> c.getId() + "=>" + c.getName())
            .forEach(System.out::println);
}

如果出于某种正当原因您真的需要超类来保存这些字段,您必须实现一个后处理器(受PostConstructAdapterFactory 启发)。说,

abstract class AbstractLookUp {

    int id;
    String name;

    abstract int getId();

    abstract String getName();

    final void postSetUp() {
        id = getId();
        name = getName();
    }

}
final class CountryByClass
        extends AbstractLookUp {

    @SerializedName("ID")
    private final Integer id = null;

    @SerializedName("CountryNameEN")
    private final String name = null;

    @Override
    int getId() {
        return id;
    }

    @Override
    String getName() {
        return name;
    }

}
final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(new TypeAdapterFactory() {
            @Override
            public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
                // Check if it's a class we can handle: AbstractLookUp
                if ( AbstractLookUp.class.isAssignableFrom(typeToken.getRawType()) ) {
                    // Get the downstream parser for the given type
                    final TypeAdapter<T> delegateTypeAdapter = gson.getDelegateAdapter(this, typeToken);
                    return new TypeAdapter<T>() {
                        @Override
                        public void write(final JsonWriter out, final T value)
                                throws IOException {
                            delegateTypeAdapter.write(out, value);
                        }

                        @Override
                        public T read(final JsonReader in)
                                throws IOException {
                            // Deserialize it as an AbstractLookUp instance
                            final AbstractLookUp abstractLookUp = (AbstractLookUp) delegateTypeAdapter.read(in);
                            // And set it up
                            abstractLookUp.postSetUp();
                            @SuppressWarnings("unchecked")
                            final T result = (T) abstractLookUp;
                            return result;
                        }
                    };
                }
                return null;
            }
        })
        .create();
final Type countryListType = new TypeToken<List<CountryByClass>>() {
}.getType();
try ( final Reader reader = getPackageResourceReader(Q43247712.class, "countries.json") ) {
    gson.<List<CountryByClass>>fromJson(reader, countryListType)
            .stream()
            .map(c -> ((AbstractLookUp) c).id + "=>" + ((AbstractLookUp) c).name)
            .forEach(System.out::println);
}

两个例子都产生

5=>英国
6=>美国

但是我发现第一种方法设计得更好并且更容易使用,而第二种方法演示了如何配置 Gson 以实现复杂的(反)序列化策略。

【讨论】:

  • 最佳解决方案!谢谢Lyubomyr,你节省了我的时间。感谢您的支持和帮助。
猜你喜欢
  • 2017-06-10
  • 1970-01-01
  • 1970-01-01
  • 2018-09-01
  • 2020-11-10
  • 2011-02-12
  • 2012-01-02
  • 1970-01-01
  • 2014-05-10
相关资源
最近更新 更多