【问题标题】:How to draw Gson model for this difficult json (mapping RUB,USD,EUR to map)?如何为这个困难的 json 绘制 Gson 模型(将 RUB、USD、EUR 映射到映射)?
【发布时间】:2017-03-29 12:55:22
【问题描述】:
{
  "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"
}

【问题讨论】:

  • 试用在线工具:jsonschema2pojo.org
  • @RavindraKushwaha 这与提供的参考文献不同。来自你。
  • @HareshChhelana 。提问者想解析 Json ..它看起来像我提到的链接......因此我将其标记为重复
  • @RavindraKushwaha 这不是重复的。我确实相信 OP 正在询问如何 map 对它们各自的 POJO 的响应,这些 POJO 具有非平凡的映射,如泛型、未包装的映射(参见混合键,如 contextcreatedRUBEURUSD)。

标签: java json gson


【解决方案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)
2017 年 3 月 29 日星期三 13:17:41 EEST
* 卢布 * 欧元 * 美元 2017 年 3 月 29 日星期三 12:19:21 EEST
* 卢布 * 欧元 * 美元 2017 年 3 月 29 日星期三 10:26:45 EEST
* 卢布 * 欧元 * 美元

【讨论】:

  • @RavindraKushwaha 谢谢!我唯一不喜欢这里的是 Rate 是如何反序列化的:其他类使用标准 Gson 工具通过反射反序列化,但这甚至需要创建一个特殊的构造函数并将属性名称硬编码在不幸的是,反序列化器。
猜你喜欢
  • 2021-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-19
  • 1970-01-01
  • 2023-03-22
  • 2019-05-27
相关资源
最近更新 更多