最近线上项目频繁报警,运维大神在报警时导出了 java线程堆栈(jstack工具导出)。统计分析后,总共运行中2003个线程,有1997个都是在做fastjson的反序列化操作。(简易分析工具可以使用threaddump-analyzer,github上的一个小项目)fastjson江湖传言是最快的json库。然而这? why?! why?!经过几个简单测试发现,当序列化和反序列化时,把属性的类型写入到json 时,性能特别特别差!

示例用法:// fastjson 序列化JSON.toJSONString(javaObj, SerializerFeature.WriteClassName);// 类似这样{"@type":"java.util.HashMap","data":{"@type":"...// fastjson 反序列化ParserConfig config = new ParserConfig() ;config.setAutoTypeSupport(true);Map map = JSON.parseObject(json, Map.class, config);

使用相同的特性。在 jackson 框架下,发现性能依然贼好!!最终决定,全面更换 jackson。由于使用json的地方特别多。大致的更换方案是,完全重写 fastjson 相关的类。尽可能少改动项目现有代码。像 fastjson 中的 JSON, JSONObject, JSONArray 都保持原包名和 类名重写相关使用的方法。重写完后,以为大功告成,去掉 fastjson的依赖,以为可以上线了!!万万没有想到,这只是万里长征的第一步!!也让我深深体会到 fastjson 的兼容性是有多么多么的好。各种神操作,让你在 jackson 中,都不知道如何改写。也怪我们使用 fastjson的方式太邪乎了!各种非常规使用。下面讲几个特殊的解析:

  1. javabean的属性是String, 反序列化时,传入这个属性的值是json对象或者json 数组;

  2. 属性是枚举,传入 json 对象;

  3. 属性是对象,传入是String,String是一个json串;

  4. 属性是集合,传入是单个json对象;jackson有配置可以使用configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY,  true)解决问题,但是这样影响处理属性是集合类型,而传入是String的问题。我就没有使用该特性。然后在自定义异常处理中统一处理该问题

    // 问题示例描述public class Abc{    private String attrArr;// 问题1 传入json数组[{"a":"123123"}]    private String attr;// 问题1 传入json对象{"a":"123123"}    private SexEnum sexEnum;// 问题2  传入json对象{"value":1,"label":"男"}      private BBB bbb;  // 问题3 传入字符串"{\"b\":\"abc\"}"     private List bbb; // 问题4 传入json对象{"b":"abc"}     // ...get/set...方法    }public class BBB{    private String b;    // ... get/set...方法    }     在fastjson 中是都可以正常转化

    以上举例的反序列化在jackson中直接报错,在网上未能找到相应问题的解决办法,但是最终通过跟踪报错的堆栈信息,进行调试后,发现可以添加一个异常处理的方法。解决这4个问题代码如下:

    // 对应jackson 的版本 2.11.3, 早期版本可能没有该方法或者参数列表不一致objectMapper.addHandler(new CustomDeserializationProblemHandler());static class CustomDeserializationProblemHandler extends DeserializationProblemHandler {        @Override        public Object handleUnexpectedToken(DeserializationContext ctxt, JavaType targetType, JsonToken t, JsonParser p, String failureMsg) throws IOException {            String json = p.getText();            if(targetType.isEnumType() && "{".equals(json)) {                ObjectMapper mapper = (ObjectMapper) p.getCodec();                mapper.readTree(p);// 直接读取一个节点。这样就不会忽略之后的结点解析了,但是并不能正确反序列化枚举,需要通过注解实现                return null;            }            if(targetType.isCollectionLikeType() && "{".equals(json)) {                ObjectMapper mapper = (ObjectMapper) p.getCodec();                Object obj =  mapper.readValue(p, targetType.getContentType());// 把单个结点数据,转化为一个数组对象                List<Object> list = new ArrayList<Object>();                list.add(obj);                return list;            }            if(targetType.getRawClass().equals(String.class) && "{".equals(json)) {                ObjectMapper mapper = (ObjectMapper) p.getCodec();                TreeNode node =  mapper.readTree(p);// 把一个对象json,转成一个字符串,有性能损失                return node.toString();            }            if(targetType.getRawClass().equals(String.class) && "[".equals(json)) {                ObjectMapper mapper = (ObjectMapper) p.getCodec();                TreeNode node =  mapper.readTree(p);// 把一个对象json数组,转成一个字符串,有性能损失                return node.toString();            }            if ((json.startsWith("{") && json.endsWith("}")) || (json.startsWith("[") && json.endsWith("]"))) {                return JSON.parseObject(p.getText(), targetType);            }            return DeserializationProblemHandler.NOT_HANDLED;        }    }
  5. 日期类型反序列化支持多种格式;
    在 fastjson 中,长整型、yyyy-MM-dd、yyyy-MM-dd HH:mm 等格式都能正确转化为日期。但是在 jackson 中默认长整型是可以正常转换的。但是另外两种并不能转换,解决办法如下:

     SimpleModule serializerModule = new SimpleModule("DateSerializer", PackageVersion.VERSION);        serializerModule.addDeserializer(Date.class, new CustomDateDeSerializer());        objectMapper.registerModule(serializerModule);static class CustomDateDeSerializer extends DateDeserializers.DateDeserializer {        private static final long serialVersionUID = 1L;        @Override        public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {            if (p != null) {                String calendatStr = p.getText();                if (calendatStr != null && calendatStr.indexOf("T") < 0 && calendatStr.length() == 19) {                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");                    try {                        return sdf.parse(calendatStr);                    } catch (ParseException e) {                    }                }                if (calendatStr != null && calendatStr.indexOf("T") < 0 && calendatStr.length() == 16) {                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");                    try {                        return sdf.parse(calendatStr);                    } catch (ParseException e) {                    }                }                if (calendatStr != null && calendatStr.length() == 10 && calendatStr.charAt(4) == '-' && calendatStr.charAt(7) == '-') {                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");                    try {                        return sdf.parse(calendatStr);                    } catch (ParseException e) {                    }                }            }            return super.deserialize(p, ctxt);        }    }    
  6. jackson最新版本中做类型保留,这样配置;

    // jackson 2.11.3 配置该信息后,序列化和反序列化都会带有类信息objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,  ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY)
  7. 枚举类型序列化是以类(即输出属性)形式输出;

    // 在枚举上添加该注解@JsonFormat(shape = Shape.OBJECT)public enum SexEnum{ ..... }

    最后分享一下关于JSON使用的愚见

    1)如果项目中在使用 fastjson 但是没有使用SerializerFeature.WriteClassName特性,没有必要更换新的json 库。但是建议升级最新版本。因为不久前报过多次严重的安全漏洞。

    2)保留类信息的方式,反序列化可能成为黑客攻击的入口。该种方式仅用于项目内部接口的调用。

    3)应规范使用json 的序列化和反序列化。上边提到的第1点。不管在 fastjson还是上边提到的 jackson 解决方案都影响性能。
    4)对于{"action":"add","data":{...大量信息....}}这样json想先确认action值,再对 data做相应类型的解析,建议新建一个类似class SimpleCommand{String action;...get/set...}这样只有 action 一个属性的类进行解析,然后根据 action 值再进行整个json的解析。速度更快一些。

(全文完)

↓↓↓推荐↓↓↓

一码解千愁

fastjson list转json字符串_从fastjson转jackson的血泪史相关推荐

  1. fastjson list转json字符串_程序员:JSON、JSONObject 与 JSONArray 简单讲解

    fastjson简介 fastjson 是阿里巴巴的开源JSON解析库,它可以解析 JSON 格式的字符串,支持将 Java Bean 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 ...

  2. fastjson解析多层Json字符串

    Json字符串: {"status":0,"result":{"location":{"lng":120.3252155 ...

  3. java处理json字符串_常见java对象转换为json字符串处理!!!

    1.JSON简介 1. 概念: JavaScript Object NotationJavaScript对象表示法 Person p = new Person(); p.setName("张 ...

  4. java中转json字符串_如何在Java中转义JSON字符串-Eclipse IDE技巧

    java中转json字符串 在Java应用程序中工作或进行JSON解析时,通常很常见的做法是从某些资源(例如RESTful Web服务)中复制粘贴JSON字符串,然后使用Jackson库解析JSON. ...

  5. android jackson 解析json字符串,一文搞定Jackson解析JSON数据

    Json解析-Jackson使用教程 日常求赞,感谢老板. 一.JSON解析 我这里指的解析是:JSON和JavaObject之间的序列化和反序列化. 如果你的项目进行了前后端分离,那你一定使用过JS ...

  6. json字符串与java对象的相互转换(jackson)

    1.java对象转换为json字符串 package com.chichung.json;import com.fasterxml.jackson.core.JsonProcessingExcepti ...

  7. 使用fastjson工具类json字符串和对象之间的转换

    文章目录 一.引入依赖 二.创建user实体类 三.测试 一.引入依赖 <!--字符串和对象操作工具类 Start--><dependency><groupId>c ...

  8. java json 内部类_使用Fastjson解析内部类的一个小问题

    使用的Fastjson的版本是1.2.7和1.2.47两个版本. 1. 问题 使用Fastjson解析包含内部类的对象时,发生异常,代码大致如下: public class HelloControll ...

  9. Fastjson使用:将json字符串转对象为空问题

    问题背景 在一次更改老项目中,因为想使用构造器方便点,就在原来的代码基础上增加了一个有参构造器,原来的代码是没有设置构造器的.(没有设置的话使用默认的一个无参构造器) . 然后因为我新创建的有参构造器 ...

最新文章

  1. 字节跳动_掌握Java字节码
  2. Springmvc与Struts2不同
  3. c++作业5 9.22
  4. python中cgi到底是什么_什么是CGI?详细介绍Python CGI编程
  5. DLL入门浅析(4)——从DLL中导出类
  6. JAVA学习笔记--初识容器类库
  7. zabbix监控MogDB之采集prometheus数据
  8. (88)FPGA乘法器设计(移位相加乘法器)
  9. 拳王虚拟项目公社:知识付费之出售虚拟教程资源赚钱,小白轻松赚钱的方式
  10. Django项目实践3 - Django模型(view-数据库)
  11. mask rcnn数据转换为tfrecord数据
  12. 二维码解析易语言代码
  13. ubuntu翻译软件安装
  14. 织梦后台登录出现500错误解决方案
  15. 你对Web3的迅速发展一无所知,逃离大厂去拥抱Web3
  16. 骚操作之Python微信远程控制摄像头!然后嘿嘿嘿!
  17. Windflowers(歌词)
  18. 网页集成大华摄像头以及回放功能2019.11.14
  19. 学计算机会学dos,DOS操作系统和上课学习的应用软件_CPUCPU评测-中关村在线
  20. GObject对象系统

热门文章

  1. 常用数据结构有哪些(转)
  2. lucene3.0_IndexSearcher排序
  3. [原创]软件测试工具简介及下载地址(不定时更新)
  4. 空指针异常,源头没有值
  5. 小程序使用echarts
  6. Java基础之PDF文件的合并
  7. telnet远程登录协议
  8. jmeter 通过ant集成到jenkins
  9. 3G手机Android应用开发视频教程_黎活明老师的视频(第五天课程)总共有八天课程...
  10. IOS学习笔记05---C语言程序的开发运行过程