首页 > 解决方案 > Gson 中的自定义 Java 映射反序列化器

问题描述

是否可以通过从每对中选择值来将对象对数组转换为 a Map,而无需创建中间 POJO?

例如,假设有 2 个Java类 ( Key.class, Value.class) 和一个JSON由其对象的序列化对构造的数组,如下所示

[
  {"key":{}, "value":{}}, // Objects k1, v1
  {"key":{}, "value":{}}, // Objects k2, v2
  {"key":{}, "value":{}}, // Objects k3, v3
  {"key":{}, "value":{}}, // Objects k4, v4
]

可以Gson“直接”(即,没有 POJO 定义)将上述数组反序列化为 a Map,其中实际的键和值是通过对相应KeyValue对象而不是“完整”对象

换句话说,给定两种Java类型KandV可能与Keyand不同Value,每对的反序列化应该是

(k1:Key, v1:Value) --> (k1':K, v1':V)

在哪里

k1' = function(k1)
v1' = function(v1)

像这样的自定义适配器不起作用:

GsonBuilder builder = new GsonBuilder();
Type type = (new TypeToken<Map<K, V>>() {}).getType();    
builder.registerTypeAdapter(type, new JsonDeserializer<Map<Key, Value>>() {
  @Override
  public Map<K, V> deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
    // Custom deserialization block,
    // invoking the `function()` method
  }
Gson gson = builder.create();

使用上述适配器会导致Gson将整个数组传递给deserialize()方法,在该方法中必须手动操作,既繁琐又容易出错。

有一个POJO

public class KeyValue {
  Key k;
  Value v;
}

上面的JSON数组可以反序列化为一个数组或一组KeyValue对象,然后可以将其转换为Map<K, V>. KeyValue但是如果没有POJO也可以这样做吗?

标签: javajsonserializationgsondeserialization

解决方案


如果您想避免使用您所指的 POJO 类,因为KeyValue我认为使用 gson 做到这一点的唯一方法是创建一个适配器。原因是您的 JSON 不符合任何通用的内置类,因此如果没有自定义逻辑,gson 将无法理解它。

由于您有KeyValue类,因此您可以在自定义适配器中利用它。我想你会发现这减少了代码的乏味。

public static void main(String[] args) throws IOException {
        GsonBuilder builder = new GsonBuilder();
        Type type = (new TypeToken<Map<Key, Value>>() {}).getType();    
        builder.registerTypeAdapter(type, new JsonDeserializer<Map<Key, Value>>() {
          @Override
          public Map<Key, Value> deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
              Map<Key, Value> resultMap = new HashMap<>();
              JsonArray jArr = json.getAsJsonArray();
              for(int i=0; i < jArr.size(); i++){
                  JsonObject item = jArr.get(i).getAsJsonObject();
                  processKeyValue(item, resultMap);
              }
              return resultMap;
          }

          private void processKeyValue(JsonObject mapEntry, Map<Key, Value> resultMap){
              Gson gson = new Gson();
              Key key = gson.fromJson(mapEntry.get("key"), Key.class);
              Value value = gson.fromJson(mapEntry.get("value"), Value.class);
              resultMap.put(key, value);
          }
        });
        Gson gson = builder.create();

        //Note: assume that the variable "json" is a JSON string. I removed my code for getting the string since I'm just reading from a file and I don't know how you're getting your data.

        // Deserialization

        Map<Key,Value> outputMap = gson.fromJson(json, type);


        System.out.println("RESULTS: "+outputMap.toString());
    }

我使用了您的示例数据,但稍作修改以获得更多内容。Key我为and创建了两个非常简单的 POJO 类Value- 它们只有一个String名为 的字段id、该字段的 getter 和 setter 以及一个toString方法。

这是我在运行上述代码时使用的输入:

[
  {"key":{"id":"k1"}, "value":{"id":"v1"}}, 
  {"key":{"id":"k2"}, "value":{"id":"v2"}},
  {"key":{"id":"k3"}, "value":{"id":"v3"}}, 
  {"key":{"id":"k4"}, "value":{"id":"v4"}}
]

输出是:

RESULTS: {Key [id=k2]=Value [id=v2], Key [id=k1]=Value [id=v1], Key [id=k3]=Value [id=v3], Key [id=k4]=Value [id=v4]}

推荐阅读