如何为这个困难的json绘制Gson模型(将RUB,USD,EUR映射到地图)?

时间:2017-03-29 12:55:23

标签: java json gson

{
  "page": 1,
  "pages": 1,
  "rows": [
    {
      "context": false,
      "created": 1490782661,
      "RUB": {
        "currency": "RUB",
        "created": 1490761924,
        "rate_buy": 5.53,
        "rate_sell": 5.57,
        "context": false
      },
      "EUR": {
        "currency": "EUR",
        "created": 1490770163,
        "rate_buy": 340,
        "rate_sell": 342,
        "context": false
      },
      "USD": {
        "currency": "USD",
        "created": 1490782661,
        "rate_buy": 315.3,
        "rate_sell": 316.1,
        "context": false
      }
    },
    {
      "context": false,
      "created": 1490779161,
      "RUB": {
        "currency": "RUB",
        "created": 1490761924,
        "rate_buy": 5.53,
        "rate_sell": 5.57,
        "context": false
      },
      "EUR": {
        "currency": "EUR",
        "created": 1490770163,
        "rate_buy": 340,
        "rate_sell": 342,
        "context": false
      },
      "USD": {
        "currency": "USD",
        "created": 1490779161,
        "rate_buy": 315.1,
        "rate_sell": 315.9,
        "context": false
      }
    },
    {
      "context": false,
      "created": 1490772405,
      "RUB": {
        "currency": "RUB",
        "created": 1490761924,
        "rate_buy": 5.53,
        "rate_sell": 5.57,
        "context": false
      },
      "EUR": {
        "currency": "EUR",
        "created": 1490770163,
        "rate_buy": 340,
        "rate_sell": 342,
        "context": false
      },
      "USD": {
        "currency": "USD",
        "created": 1490772405,
        "rate_buy": 314.9,
        "rate_sell": 315.7,
        "context": false
      }
    }
  ],
  "total": "132"
}

1 个答案:

答案 0 :(得分:1)

不要总是信任自动副模式生成器,因为它们无法为非平凡的情况生成强大而有效的映射。因此,这里非常受欢迎的建议在线工具也不能产生良好的映射,因为至少:

  • 最顶层的JSON对象看起来像是通用服务器响应,因此可以进行参数化。
  • JSON文档中的日期以Unix纪元风格表示,因此从该工具的角度来看,它将是intlong
  • 货币名称将被硬编码为字段,因此您必须绑定3种货币,除非您正在增强映射,但仍然会失去Map灵活性。

首先,让我们定义映射:

final class Response<T> {

    // Note a few things:
    // - Gson can deserialize final fields, and these are incoming data supposed to be read-only
    // - There are no getters for brevity, these are just incoming data bags anyway
    // - If `final`, primitive types like `int` should not be initialized with `0` (javac inlines such constants)
    //   so we're cheating making them look like constants...
    @SerializedName("page") final int page = Integer.valueOf(0);
    @SerializedName("pages") final int pages = Integer.valueOf(0);
    @SerializedName("rows") final List<T> rows = null;
    @SerializedName("total") final int total = Integer.valueOf(0);

}

与前一个类不同,Rate类无法使用Gson通常处于最佳状态的反射策略进行反序列化。该类有一个构造函数,所以我们可以自己实例化它:

final class Rate {

    final boolean context;
    final Date created;
    final Map<String, Currency> currencies;

    Rate(final boolean context, final Date created, final Map<String, Currency> currencies) {
        this.context = context;
        this.created = created;
        this.currencies = currencies;
    }

}
final class Currency {

    @SerializedName("currency") final String currency = null;
    @SerializedName("created") final Date created = null;
    @SerializedName("rate_buy") final double rateBuy = Double.valueOf(0);
    @SerializedName("rate_sell") final double rateSell = Double.valueOf(0);
    @SerializedName("context") final boolean context = Boolean.valueOf(false);

}

接下来,需要两个反序列化器:日期和费率。

final class DateJsonDeserializer
        implements JsonDeserializer<Date> {

    // No state? So it can be a singleton
    private static final JsonDeserializer<Date> dateJsonDeserializer = new DateJsonDeserializer();

    private DateJsonDeserializer() {
    }

    // But not letting client code know if it's a singleton or not -- it's encapsulated
    static JsonDeserializer<Date> getDateJsonDeserializer() {
        return dateJsonDeserializer;
    }

    @Override
    public Date deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
            throws JsonParseException {
        // jsonElement.getAsLong() would work either
        // But even longs can have their own deserialization strategy
        // Note `* 1000` -- `java.util.Date` accepts milliseconds while Unix epoch is "seconds-based"
        return new Date((Long) context.deserialize(jsonElement, long.class) * 1000);
    }

}
final class RateJsonDeserializer
        implements JsonDeserializer<Rate> {

    private static final JsonDeserializer<Rate> rateJsonDeserializer = new RateJsonDeserializer();

    private RateJsonDeserializer() {
    }

    static JsonDeserializer<Rate> getRateJsonDeserializer() {
        return rateJsonDeserializer;
    }

    @Override
    public Rate deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
            throws JsonParseException {
        final JsonObject jsonObject = jsonElement.getAsJsonObject();
        boolean rateContext = false;
        Date rateCreated = null;
        final Map<String, Currency> rateCurrencies = new LinkedHashMap<>();
        // Analyzing rate JSON documents for each key/value pair
        for ( final Entry<String, JsonElement> entry : jsonObject.entrySet() ) {
            final String key = entry.getKey();
            final JsonElement value = entry.getValue();
            switch ( key ) {
            case "context":
                rateContext = context.deserialize(value, boolean.class);
                break;
            case "created":
                rateCreated = context.deserialize(value, Date.class);
                break;
            default:
                rateCurrencies.put(key, context.deserialize(value, Currency.class));
                break;
            }
        }
        // So that we can "unwrap" its layout and "flatten" the currencies map
        return new Rate(rateContext, rateCreated, rateCurrencies);
    }

}

现在,它的使用方式:

private static final TypeToken<Response<Rate>> currencyRatesResponseTypeToken = new TypeToken<Response<Rate>>() {
};

private static final Gson gson = new GsonBuilder()
        .registerTypeAdapter(Date.class, getDateJsonDeserializer())
        .registerTypeAdapter(Rate.class, getRateJsonDeserializer())
        .create();

public static void main(final String... args) {
    final Response<Rate> response = gson.fromJson(JSON, currencyRatesResponseTypeToken.getType());
    System.out.println(response.page + "/" + response.pages + " (" + response.total + ")");
    for ( final Rate rate : response.rows ) {
        System.out.println(rate.created);
        for ( final Entry<String, Currency> entry : rate.currencies.entrySet() ) {
            final Currency currency = entry.getValue();
            System.out.println("* " + entry.getKey() + " <== " + currency.currency + " (" + currency.rateBuy + "/" + currency.rateSell + ") " + currency.created);
        }
    }
}

演示输出:

  

1/1(132)
  3月29日星期三13:17:41 EEST 2017年   * RUB&lt; == RUB(5.53 / 5.57)Wed Mar 29 07:32:04 EEST 2017
  * EUR&lt; == EUR(340.0 / 342.0)3月29日星期三09:49:23 EEST 2017年   * USD&lt; == USD(315.3 / 316.1)Wed Mar 29 13:17:41 EEST 2017
  3月29日星期三12:19:21 EEST 2017年   * RUB&lt; == RUB(5.53 / 5.57)Wed Mar 29 07:32:04 EEST 2017
  * EUR&lt; == EUR(340.0 / 342.0)3月29日星期三09:49:23 EEST 2017年   * USD&lt; == USD(315.1 / 315.9)3月29日星期三12:19:21 EEST 2017
  3月29日星期三10:26:45 EEST 2017年   * RUB&lt; == RUB(5.53 / 5.57)Wed Mar 29 07:32:04 EEST 2017
  * EUR&lt; == EUR(340.0 / 342.0)3月29日星期三09:49:23 EEST 2017年   * USD&lt; == USD(314.9 / 315.7)3月29日星期三10:26:45 EEST 2017