fastjson list轉json字串_從fastjson轉jackson的血淚史
技術標籤:fastjson list轉json字串fastjson string轉json物件fastjson 字串轉mapfastjson最新版本jackson 讀取多檔案java jsonarray轉list
最近線上專案頻繁報警,運維大神在報警時匯出了 java執行緒堆疊 (jstack工具匯出) 。統計分析後,總共執行中2003個執行緒,有1997個都是在做fastjson的反序列化操作。 (簡易分析工具可以使用threaddump-analyzer,github上的一個小專案) fastjson江湖傳言是最快的json庫。然而這? why?! why?! 經過幾個簡單測試發現,當序列化和反序列化時,把屬性的型別寫入到json 時,效能特別特別差!使用相同的特性。在 jackson 框架下,發現效能依然賊好!!最終決定,全面更換 jackson。 由於使用json的地方特別多。大致的更換方案是,完全重寫 fastjson 相關的類。儘可能少改動專案現有程式碼。像 fastjson 中的 JSON, JSONObject, JSONArray 都保持原包名和 類名重寫相關使用的方法。 重寫完後,以為大功告成,去掉 fastjson的依賴,以為可以上線了!!萬萬沒有想到,這只是萬里長征的第一步!!也讓我深深體會到 fastjson 的相容性是有多麼多麼的好。各種神操作,讓你在 jackson 中,都不知道如何改寫。也怪我們使用 fastjson的方式太邪乎了!各種非常規使用。 下面講幾個特殊的解析:示例用法://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);
javabean的屬性是String,反序列化時,傳入這個屬性的值是json物件或者json 陣列;
屬性是列舉,傳入 json 物件;
屬性是物件,傳入是String,String是一個json串;
屬性是集合,傳入是單個json物件;jackson有配置可以使用configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)解決問題,但是這樣影響處理屬性是集合型別,而傳入是String的問題。我就沒有使用該特性。然後在自定義異常處理中統一處理該問題
以上舉例的反序列化在jackson中直接報錯,在網上未能找到相應問題的解決辦法,但是最終通過跟蹤報錯的堆疊資訊,進行除錯後,發現可以新增一個異常處理的方法。解決這4個問題程式碼如下://問題示例描述public class Abc{privateStringattrArr;//問題1傳入json陣列[{"a":"123123"}]privateStringattr;//問題1傳入json物件{"a":"123123"}privateSexEnumsexEnum;//問題2傳入json物件{"value":1,"label":"男"}privateBBBbbb;//問題3傳入字串"{\"b\":\"abc\"}"privateListbbb;//問題4傳入json物件{"b":"abc"}//...get/set...方法}public classBBB{privateStringb;//...get/set...方法}在fastjson中是都可以正常轉化
//對應jackson 的版本 2.11.3, 早期版本可能沒有該方法或者引數列表不一致objectMapper.addHandler(newCustomDeserializationProblemHandler());staticclassCustomDeserializationProblemHandlerextendsDeserializationProblemHandler{ @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();TreeNodenode=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; } }
日期型別反序列化支援多種格式;
在 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); } }
jackson最新版本中做型別保留,這樣配置;
//jackson 2.11.3 配置該資訊後,序列化和反序列化都會帶有類資訊objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL,JsonTypeInfo.As.PROPERTY)
列舉型別序列化是以類(即輸出屬性)形式輸出;
//在列舉上新增該註解@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的解析。速度更快一些。
(全文完)
↓↓↓推薦↓↓↓一碼解千愁