示例用法:// 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 中是都可以正常转化
  1. 以上举例的反序列化在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;        }    }
  1. 日期类型反序列化支持多种格式;
    在 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);        }    }
  1. jackson最新版本中做类型保留,这样配置;
// jackson 2.11.3 配置该信息后,序列化和反序列化都会带有类信息objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,  ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY)
  1. 枚举类型序列化是以类(即输出属性)形式输出;
// 在枚举上添加该注解@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的解析。速度更快一些。

(全文完)