1. 程式人生 > 其它 >fastjson list轉json字串_從fastjson轉jackson的血淚史

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 時,效能特別特別差!
示例用法://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{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中直接報錯,在網上未能找到相應問題的解決辦法,但是最終通過跟蹤報錯的堆疊資訊,進行除錯後,發現可以新增一個異常處理的方法。解決這4個問題程式碼如下:
    //對應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;        }    }
  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的解析。速度更快一些。

(全文完)

↓↓↓推薦↓↓↓

b0f3d1cb0b8eac082e26946219778299.png

一碼解千愁