首页 > 学院 > 开发设计 > 正文

欢迎使用CSDN-markdown编辑器

2019-11-10 18:10:46
字体:
来源:转载
供稿:网友

解决Gson 处理Map将整型处理为浮点型的问题

gson一直是我用着非常得心应手的json处理工具。但是最近遇到了一个坑,就是在处理java.util.Map型json字符串的时候会把整型转为浮点型。示例程序如下:Javapublic static void main(String[] args) { Gson gson = new Gson(); String json = "{/"key1/":1,/"key2/":2.0,/"key3/":/"3/"}"; Map m = gson.fromJson(json, Map.class); System.out.PRintln(m.get("key1")); System.out.println(m.get("key2")); System.out.println(m.get("key3"));}执行结果如下:1.02.03Process finished with exit code 0原因在于json的语法中关于数值只有一个number类型,而不会去判断这个number具体是整型是浮点型还是长整型。而Gson处理的时候也确实是偷懒了,统一将之视为浮点型。可以在Gson的ObjectTypeAdapter类中看到:Java@Overridepublic Object read(JsonReader in) throws IOException { JsonToken token = in.peek(); switch (token) { case BEGIN_ARRAY: List<Object> list = new ArrayList<Object>(); in.beginArray(); while (in.hasNext()) { list.add(read(in)); } in.endArray(); return list; 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; case STRING: return in.nextString(); case NUMBER: return in.nextDouble(); case BOOLEAN: return in.nextBoolean(); case NULL: in.nextNull(); return null; default: throw new IllegalStateException(); }}在case NUMBER这一行可以看到,数值型的数据都被处理为Double型的值。此外因为这里使用的Map的value类型是Object,在示例代码中就涉及到了Integer、Double和String。所以也不能使用TypeToken这样的解决方案。使用TypeToken的方式如下,适用于有明确的泛型说明的情况:JavaMap m = gson.fromJson(json, new TypeToken<Map<String, Integer>>(){}.getType());其它的解决方案也简单:1. 使用其他的json库,比如jackson和fastjson,亲测过,都没有这种问题;2. 添加自定义模块,修改这个问题。详细说明下方案2。Gson支持添加自定义解析方案,可以使用GsonBuilder的registerTypeAdapter和registerTypeHierarchyAdapter。前者只针对设置的类进行序列化及反序列化,后者可以对设置的类及其子类进行序列化。可以添加的解析类的类型包括JsonSerializer、JsonDeserializer和TypeAdapter这三个接口的实现类。下面是一个使用自定义的JsonDeserializer方案:Javaimport com.google.gson.*;import com.google.gson.reflect.TypeToken;import java.lang.reflect.Type;import java.util.HashMap;import java.util.Map;public class Test { public static void main(String[] args) { Gson gson = new GsonBuilder().registerTypeAdapter(Map.class, new JsonDeserializer<Map>() { @Override public Map deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject jsonObject = json.getAsJsonObject(); Map p = new HashMap(); for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) { if (e.getValue().isJsonPrimitive()) { p.put(e.getKey(), e.getValue()); } p.put(e.getKey(), e.getValue().getAsString()); } return p; } }).create(); String json = "{/"key1/":1,/"key2/":2.0,/"key3/":/"zhongwen/"}"; Map m = gson.fromJson(json, new TypeToken<Map<String, Object>>() { }.getType()); System.out.println(m.get("key1")); System.out.println(m.get("key2")); System.out.println(m.get("key3")); }}个方案也是有局限性的,只适用于Map的key和value都是数值型和字符串的情况(也可以添加对boolean的支持)。对于复杂的Map结构就有些无力了。此外另一个方案是根据ObjectTypeAdapter自定义TypeAdapter。这个我也试过,功能支持还算可以,它解析json的方式是分层实现的。不过如果目标是具体类还可以,对于抽象类或接口的适用性就差一些。因为关键是json最外层的解析,如果是json对应的是对象的话,也就只能封装成一个目标类型对象或其超类对象。先就这样。

原文:http://www.zhyea.com/2016/11/13/gson-map-integer-double.html


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表