我在使用GSON解析包含时间戳作为键的JSON响应时,收到了一个重复键异常。它给出了以下错误:com.google.gson.JsonSyntaxException:重复键:1463048935在网站上的Map类型适配Adapter.read器Adapter.read工厂我如何使它忽略重复的条目,并只将其解析为包含重复条目中任何一个的Map?
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代码示例:
fromJson
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决定添加一个内置的方法来忽略重复项,如这里所建议的,最好切换到那个方法。
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都将通过反序列化器代码是的,看起来像一个肮脏的黑客,但它的工作另一种方法是为自定义类型注册类型加法器,使反序列化器仅在需要时被调用:
Map.class
@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; } } ```
2条答案
按热度按时间hjqgdpho1#
Hackerman解决方案,使用GSON v2.8.5进行测试和工作,但使用风险自担!无论何时将GSON更新到新版本,请确保检查此解决方案是否仍在工作!
基本上,您可以使用泛型ObjectTypeAdapter忽略重复项的事实,如下所示:
似乎MapTypeAdapterFactory会检查重复项
但是对象类型适配器不
现在,您可以像往常一样尝试使用
fromJson
进行反序列化,但要捕获“duplicate key”异常,然后将其反序列化为一个通用Object(这将忽略重复项),再将其序列化(这将生成一个没有重复键的JSON字符串),最后使用正确的类型进行反序列化(它实际上是要成为的类型)。下面是一个Kotlin代码示例:
显然,这会增加(潜在的)开销,因为需要两次反序列化和一次额外的序列化,但目前我还没有看到任何其他的方法来做到这一点。如果Google决定添加一个内置的方法来忽略重复项,如这里所建议的,最好切换到那个方法。
rdlzhqv92#
我不能使用Kotlin(正如前面回答的那样),所以我将其调整为Java。可以通过注册类型添加器来实现:
这样,到
Map.class
的所有Map都将通过反序列化器代码是的,看起来像一个肮脏的黑客,但它的工作
另一种方法是为自定义类型注册类型加法器,使反序列化器仅在需要时被调用: