分享

JSON序列化导致Long类型被搞成Integer经典巨坑

 vnxy001 2020-03-18

一、背景

最近遇到一个错误,就是某个字段肯定是Long类型的,通过Map<String,Object>方式转成JSON字符串,然后存到了DB的某个字段里。

为了Json存储字,然后反序列化为Map(不是自定义的常规对象)

比如某二方为了提供通用的接口能力,将Map<String,Object>的数据通过JSON序列化方式缓存然后JSON反序列化拿到Map。

等情况。

用的时候就通过Key获取对象之后直接强转Long,然后类型转换异常。

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long

二、上代码

  1. @Test(expected = ClassCastException.class)
  2. public void testJson() {
  3. String id = "id";
  4. String name = "name";
  5. Long idValue = 3000L;
  6. Map<String, Object> data = new HashMap<>(2);
  7. data.put(id, idValue);
  8. data.put(name, "张三");
  9. Assert.assertEquals(idValue, (Long) data.get(id));
  10. String jsonString = JSON.toJSONString(data);
  11. // 反序列化时Long被转为了Integer
  12. Map map = JSON.parseObject(jsonString, Map.class);
  13. Object idObj = map.get(id);
  14. Assert.assertTrue(idObj instanceof Integer);
  15. Assert.assertEquals(idValue, (Long) idObj);
  16. }

没用通过JSON序列化,虽然类型为Object,但是实际的类型为Long。

序列化为Json时后,Josn串是没有 Long类型的,而且反转回来也是Object接收,如果数字小于Interger的最大值,给转成了Integer!

三、方案

3.1 采用JDK自带的序列化。

https://blog.csdn.net/outsanding/article/details/80963646 提到“redis工具,在存java对象的时候,需要实体实现序列化。工具这样设计肯定有理由的。”估计大家知道啥理由了吧。

  1. public class JDKSerializeUtil {
  2. public static byte[] serialize(Object object) {
  3. ObjectOutputStream objectOutputStream = null;
  4. ByteArrayOutputStream byteArrayOutputStream = null;
  5. try {
  6. byteArrayOutputStream = new ByteArrayOutputStream();
  7. objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
  8. objectOutputStream.writeObject(object);
  9. return byteArrayOutputStream.toByteArray();
  10. } catch (IOException e) {
  11. e.printStackTrace();
  12. }
  13. return null;
  14. }
  15. public static Object deserialization(byte[] binaryByte) {
  16. ObjectInputStream objectInputStream = null;
  17. ByteArrayInputStream byteArrayInputStream = null;
  18. byteArrayInputStream = new ByteArrayInputStream(binaryByte);
  19. try {
  20. objectInputStream = new ObjectInputStream(byteArrayInputStream);
  21. return objectInputStream.readObject();
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. }
  25. return null;
  26. }
  27. }
  1. public class JDKSerializeUtilTest {
  2. private Map<String, Object> map = new HashMap<>();
  3. @Before
  4. public void init() {
  5. map.put("id", 200L);
  6. map.put("name", "张三");
  7. }
  8. @Test
  9. public void serialize() {
  10. }
  11. @Test
  12. public void unserizlize() {
  13. byte[] serialize = JDKSerializationUtil.serialize(map);
  14. Object deserialize = JDKSerializationUtil.deserialization(serialize);
  15. Map<String, Object> map = (Map<String, Object>) deserialize;
  16. Object id = map.get("id");
  17. Assert.assertEquals(java.lang.Long.class, id.getClass());
  18. }
  19. }

3.2 采用JSON反序列化Map进行映射

  1. @Test
  2. public void testJson() {
  3. String id = "id";
  4. String name = "name";
  5. Map<String, Object> data = new HashMap<>(2);
  6. data.put(id, 3000L);
  7. data.put(name, "张三");
  8. Long idLong = (Long) data.get(id);
  9. System.out.println("idLong" + idLong);
  10. String jsonString = JSON.toJSONString(data);
  11. // 反序列化时Long被转为了Integer
  12. Map map = JSON.parseObject(jsonString, Map.class);
  13. // 直接用JSON转没问题
  14. User user = new JSONObject(map).toJavaObject(User.class);
  15. System.out.println(user);
  16. //强转不行
  17. Object idObj = map.get(id);
  18. System.out.println(idObj.getClass());
  19. Long idLong2 = (Long) idObj;
  20. System.out.println("idLong2" + idLong2);
  21. }
  1. @Data
  2. public class User {
  3. private Long id;
  4. private String name;
  5. }

如果属性不一致用JSONField注解去映射。

3.3 避免使用通用的对象类型,如果必须用则一定考虑这个细节,坑坑坑!!

四、思考

总之开发中尽量不要乱用一些对象属性转换工具,导致属性漏掉或者转错。

不要滥用Map或者JSONObject来传递参数。

如果是含Object类型JSON序列化要特别小心上述问题!!

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多