如何在使用gson解析json时忽略重复的键?

kdfy810k  于 2022-11-06  发布在  其他
关注(0)|答案(2)|浏览(552)

我在使用GSON解析包含时间戳作为键的JSON响应时,收到了一个重复键异常。它给出了以下错误:
com.google.gson.JsonSyntaxException:重复键:1463048935在网站上的Map类型适配Adapter.read器Adapter.read工厂
我如何使它忽略重复的条目,并只将其解析为包含重复条目中任何一个的Map?

hjqgdpho

hjqgdpho1#

Hackerman解决方案,使用GSON v2.8.5进行测试和工作,但使用风险自担!无论何时将GSON更新到新版本,请确保检查此解决方案是否仍在工作!

基本上,您可以使用泛型ObjectTypeAdapter忽略重复项的事实,如下所示:
似乎MapTypeAdapterFactory会检查重复项

V replaced = map.put(key, value);
  if (replaced != null) {
    throw new JsonSyntaxException("duplicate key: " + key);
  }

但是对象类型适配器不

case BEGIN_OBJECT:
  Map<String, Object> map = new LinkedTreeMap<String, Object>();
  in.beginObject();
  while (in.hasNext()) {
    map.put(in.nextName(), read(in));
  }
  in.endObject();
  return map;

现在,您可以像往常一样尝试使用fromJson进行反序列化,但要捕获“duplicate key”异常,然后将其反序列化为一个通用Object(这将忽略重复项),再将其序列化(这将生成一个没有重复键的JSON字符串),最后使用正确的类型进行反序列化(它实际上是要成为的类型)。
下面是一个Kotlin代码示例:

fun <T> String.deserialize(gson: Gson, typeToken: TypeToken<T>): T {
    val type = typeToken.type
    return try {
        gson.fromJson<T>(this, type)
    } catch (e: JsonSyntaxException) {
        if (e.message?.contains("duplicate key") == true) {
            gson.toJson(deserialize(gson, object : TypeToken<Any>() {})).deserialize(gson, typeToken)
        } else {
            throw e
        }
    }
}

显然,这会增加(潜在的)开销,因为需要两次反序列化和一次额外的序列化,但目前我还没有看到任何其他的方法来做到这一点。如果Google决定添加一个内置的方法来忽略重复项,如这里所建议的,最好切换到那个方法。

rdlzhqv9

rdlzhqv92#

我不能使用Kotlin(正如前面回答的那样),所以我将其调整为Java。可以通过注册类型添加器来实现:

@Test
    void testDuplicatesIgnored() {
        String json = "{\"key\": \"value\", \"key\": \"value2\"}";

        Gson gson = new GsonBuilder()
                .registerTypeAdapter(Map.class, new JsonDeserializer<Map<String, Object>>() {
                    @Override
                    public Map<String, Object> deserialize(JsonElement json1, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
                        return new Gson().fromJson(json1, typeOfT);
                    }
                })
                .create();

        Type mapType = new TypeToken<Map<String, Object>>() {}.getType();

        Map<String, Object> map = gson.fromJson(json, mapType);
        System.out.println("map = " + map); // map = {key=value2}

        assertThat(map).hasSize(1);
        assertThat(map.get("key")).isEqualTo("value2");
    }

这样,到Map.class的所有Map都将通过反序列化器代码
是的,看起来像一个肮脏的黑客,但它的工作
另一种方法是为自定义类型注册类型加法器,使反序列化器仅在需要时被调用:

@Test
    void testDuplicatesIgnored() {
        String json = "{\"key\": \"value\", \"key\": \"value2\"}";

        Gson gson = new GsonBuilder()
                .registerTypeAdapter(MapIgnoringDuplicatesContainer.class, new JsonDeserializer<MapIgnoringDuplicatesContainer>() {
                    @Override
                    public MapIgnoringDuplicatesContainer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
                        Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
                        return new MapIgnoringDuplicatesContainer(new Gson().fromJson(json, mapType));
                    }
                })
                .create();

        Map<String, Object> map = gson.fromJson(json, MapIgnoringDuplicatesContainer.class).getMap();
        System.out.println("map = " + map); // map = {key=value2}

        assertThat(map).hasSize(1);
        assertThat(map.get("key")).isEqualTo("value2");
    }

    private class MapIgnoringDuplicatesContainer {

        private Map<String, Object> map;

        public MapIgnoringDuplicatesContainer(Map<String, Object> map) {
            this.map = map;
        }

        public Map<String, Object> getMap() {
            return map;
        }
    }
    ```

相关问题