内容简介:前言:日常开发中,与json打交道的机会很多,一般对象json转都不会出现什么问题,但是json转对象就有可能出现问题了,今天就来说说json转map导致int型转换成double的问题接下来的操作大家都知道了,借助于网络平台,于是乎找到几种解决方式,细心的我发现有人评论解决他们的问题,看来有戏啊【手动滑稽】1、需要gson解析的类型 , 重写他的deserialize方法, 就是将其中json手动解析成map , 不对数据进行处理
前言:日常开发中,与json打交道的机会很多,一般对象json转都不会出现什么问题,但是json转对象就有可能出现问题了,今天就来说说json转map导致int型转换成double的问题
问题重现
- 之前解决过long型被转化成科学计数法的问题,所有就拿以前的公用方法,一个泛型 工具 类
public class MyType<T> { public T gsonToMap(String strJson) { Gson gson = new GsonBuilder() .registerTypeAdapter(new TypeToken<T>(){}.getType(),new MapTypeAdapter()).create(); return gson.fromJson(strJson, new TypeToken<T>() { }.getType()); } } String json = "{\"identifier\":\"18111111111\",\"opType\":1,\"platform\":0}"; Map<String, Object> map = new MyType<Map<String, Object>>().gsonToMap(json); 复制代码
- 直接将需求类型对象传入泛型就好了。
- 然而事与愿违,int成功的转换成double,1->1.0、0->0.0,如上图所示
接下来的操作大家都知道了,借助于网络平台,于是乎找到几种解决方式,细心的我发现有人评论解决他们的问题,看来有戏啊【手动滑稽】
解决方案
1、需要gson解析的类型 , 重写他的deserialize方法, 就是将其中json手动解析成map , 不对数据进行处理
public HashMap<String,Object> gsonToMap(String strJson) { Gson gson = new GsonBuilder() .registerTypeAdapter( new TypeToken<HashMap<String,Object>>(){}.getType(), new JsonDeserializer<HashMap<String, Object>>() { @Override public HashMap<String, Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { HashMap<String, Object> hashMap = new HashMap<>(); JsonObject jsonObject = json.getAsJsonObject(); Set<Map.Entry<String, JsonElement>> entrySet = jsonObject.entrySet(); for (Map.Entry<String, JsonElement> entry : entrySet) { hashMap.put(entry.getKey(), entry.getValue()); } return hashMap; } }).create(); return gson.fromJson(strJson, new TypeToken<HashMap<String,Object>>() { }.getType()); } 复制代码
- 经过实践,是可以转化成功,但是本着复用的思想,我把map替换成泛型,然后就不行,一脸蒙蔽;(问题暂时搁置一旁)
2、自定义TypeAdapter替代Gson默认的adapter(此处埋下伏笔【偷笑】)解决,自定义TypeAdapter如下:
public class MapTypeAdapter extends TypeAdapter<Object> { private final TypeAdapter<Object> delegate = new Gson().getAdapter(Object.class); @Override public Object read(JsonReader in) throws IOException { JsonToken token = in.peek(); switch (token) { case BEGIN_ARRAY: List<Object> list = new ArrayList<>(); in.beginArray(); while (in.hasNext()) { list.add(read(in)); } in.endArray(); return list; case BEGIN_OBJECT: Map<String, Object> map = new LinkedTreeMap<>(); in.beginObject(); while (in.hasNext()) { map.put(in.nextName(), read(in)); } in.endObject(); return map; case STRING: return in.nextString(); case NUMBER: /** * 改写数字的处理逻辑,将数字值分为整型与浮点型。 */ double dbNum = in.nextDouble(); // 数字超过long的最大值,返回浮点类型 if (dbNum > Long.MAX_VALUE) { return String.valueOf(dbNum); } // 判断数字是否为整数值 long lngNum = (long) dbNum; if (dbNum == lngNum) { return String.valueOf(lngNum); } else { return String.valueOf(dbNum); } case BOOLEAN: return in.nextBoolean(); case NULL: in.nextNull(); return null; default: throw new IllegalStateException(); } } @Override public void write(JsonWriter out, Object value) throws IOException { delegate.write(out,value); } } 复制代码
- 然后如法炮制,仍然固执的使用泛型,并将我们自定义的注册到gson上
public T gsonToMap(String strJson) { Gson gson = new GsonBuilder() .registerTypeAdapter(new TypeToken<T>(){}.getType(),new MapTypeAdapter()).create(); return gson.fromJson(strJson, new TypeToken<T>() { }.getType()); } String json = "{\"identifier\":\"18111111111\",\"opType\":1,\"platform\":0}"; Map<String, Object> map = new MyType<Map<String, Object>>().gsonToMap(json); 复制代码
- 等待结果中...,每错就是这么刺激,int一样会转化成double
- 把泛型直接替换成目标对象类型,再试了试,证明是没问题的
public static Map<String, Object> gsonToMap(String strJson) { Gson gson = new GsonBuilder() .registerTypeAdapter(new TypeToken<Map<String,Object>>(){}.getType(),new MapTypeAdapter()).create(); return gson.fromJson(strJson, new TypeToken<Map<String, Object>>() { }.getType()); } String json = "{\"identifier\":\"18111111111\",\"opType\":1,\"platform\":0}"; Map<String, Object> map = new MyType<Map<String, Object>>().gsonToMap(json); 复制代码
上述方案的确是可以解决我的问题,但是却给我留下了疑问;本着知其然知其所以然的目的,觉得解决这些疑惑
解决疑惑
- 为什么传递泛型不行?
- 为什么是把int转化成了double,而不是其他类型比如string?
1、关于泛型这里就要提到 泛型擦除 ,及泛型只在编译阶段有效,运行时就无效了
- 跟踪源码会发现 TypeAdapter 就已经是一个泛型抽象类了
public abstract class TypeAdapter<T> 复制代码
- 我在外层又传了一次泛型,运行时根本就不认识我传递的目标对象类型了
- 在外层直接传递目标对象类型,这里我传递的是HashMap<String,Object>,可我完全正确的识别出来
- 所以我这里的操作完全是符合泛型擦除,所以运行时代码根本不认识这是个什么东西,自然不回你达到我们想要的效果了
2、int转double,其实这是 Gson 在源码中故意为之的,其实不仅是 int , long 也会转化成 double ,接下来我们去寻找证据
- 跟踪源码,走你 => 过程省略1000步,忽略1000000字,我们会来到Gson下的这个地方
- 这里处理Number型的adapter,除此之外还有
处理double: private TypeAdapter<Number> doubleAdapter(boolean serializeSpecialFloatingPointValues) { if (serializeSpecialFloatingPointValues) { return TypeAdapters.DOUBLE; } return new TypeAdapter<Number>() { @Override public Double read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } return in.nextDouble(); } @Override public void write(JsonWriter out, Number value) throws IOException { if (value == null) { out.nullValue(); return; } double doubleValue = value.doubleValue(); checkValidFloatingPoint(doubleValue); out.value(value); } }; } 处理float: private TypeAdapter<Number> floatAdapter(boolean serializeSpecialFloatingPointValues) { if (serializeSpecialFloatingPointValues) { return TypeAdapters.FLOAT; } return new TypeAdapter<Number>() { @Override public Float read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } return (float) in.nextDouble(); } @Override public void write(JsonWriter out, Number value) throws IOException { if (value == null) { out.nullValue(); return; } float floatValue = value.floatValue(); checkValidFloatingPoint(floatValue); out.value(value); } }; } 复制代码
- 其实这里就是在寻找与我们目标对象想匹配的类型,但是如果找不到相匹配的类型,就会去调用 ObjectTypeAdapter ,继续跟踪,它终于要在这里正式寻找喜欢的适配器了【斜眼笑】
- 咋们运气比较好,这 for (TypeAdapterFactory factory : factories) 里有40几个适配器,第二个就是我们寻找的 ObjectTypeAdapter
- 它一看大家都是 T 就你跟我长得最像了,那就调用你了,于是乎就来到新世界
public final class ObjectTypeAdapter extends TypeAdapter<Object> { public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { @SuppressWarnings("unchecked") @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { if (type.getRawType() == Object.class) { return (TypeAdapter<T>) new ObjectTypeAdapter(gson); } return null; } }; private final Gson gson; ObjectTypeAdapter(Gson gson) { this.gson = gson; } @Override public 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(); } } @SuppressWarnings("unchecked") @Override public void write(JsonWriter out, Object value) throws IOException { if (value == null) { out.nullValue(); return; } TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) gson.getAdapter(value.getClass()); if (typeAdapter instanceof ObjectTypeAdapter) { out.beginObject(); out.endObject(); return; } typeAdapter.write(out, value); } } 复制代码
- 是不是跟我们之前自定义的adapter一模一样,这就是为什么我们要复写这个 TypeAdapter ,重点看下面
case NUMBER: return in.nextDouble(); 复制代码
- 只要是 Number (包括 int、long、float、double 等)型,都会被强制转化成 double ,至于为什么这么做,因为这里所有的类型都可以转换成 double ,而反过来则不行。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 查找一个字符串中最长不含重复字符的子字符串,计算该最长子字符串的长度
- 字符串、字符处理总结
- 高频算法面试题(字符串)leetcode 387. 字符串中的第一个唯一字符
- php删除字符串最后一个字符
- (三)C语言之字符串与字符串函数
- 算法笔记字符串处理问题H:编排字符串(2064)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JS 压缩/解压工具
在线压缩/解压 JS 代码
HSV CMYK 转换工具
HSV CMYK互换工具