fastjson近些年頻頻爆出安全漏洞,現在已經是互聯網的過街老鼠了,建議早去早好,網上搜索了下,相對而言google出品的gson目前還沒聽說有重大安全問題。下面是gson替換fastjson可能會遇到的一些坑。
一、美化輸出格式
new GsonBuilder().setPrettyPrinting().create();
二、自定義date的輸出格式
new GsonBuilder() .setPrettyPrinting() //美化輸出格式 .setDateFormat("yyyy-MM-dd HH:mm:ss.SSS") //設置date字段的輸出格式 .create();
寫一段單元測試代碼:
package test.nio.study; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import lombok.Data; import lombok.NoArgsConstructor; import org.junit.Test; import java.util.*; public class GsonTest { public Gson gson() { return new GsonBuilder() .setPrettyPrinting() //美化輸出格式 .setDateFormat("yyyy-MM-dd HH:mm:ss.SSS") //設置date字段的輸出格式 .create(); } @Data @NoArgsConstructor public class InnerTestClass { private Integer intField; private Long longField; private String strField; private Date dateField; private List<InnerTestClass> listField; private Map<String, String> mapField; } @Test public void testGson() { InnerTestClass test = new InnerTestClass(); test.intField = 1; test.longField = 2L; test.dateField = new Date(); test.mapField = new HashMap<>(); test.mapField.put("test", "hello world"); test.listField = new ArrayList<>(); InnerTestClass test2 = new InnerTestClass(); test2.dateField = new Date(); test.listField.add(test2); System.out.println(gson().toJson(test)); } }
輸出如下:
{ "intField": 1, "longField": 2, "dateField": "2020-06-14 23:04:17.202", "listField": [ { "dateField": "2020-06-14 23:04:17.202" } ], "mapField": { "test": "hello world" } }
三、兼容多種date字符串/timestamp的反序列化
{ "dateField": "2020-06-14 23:04:17", "listField": [ { "dateField": "2020-06-14 23:04:17.202" }, { "dateField": 1592148201102 }, { "dateField": "" } ] }
如果上面這段json,嘗試用剛才生成的gson實例反序列化的話,會報錯Invalid time zone indicator ' ',原因是 第1個dateField沒有帶.SSS的毫秒部分,第3個dateField是一個timestamp的long型數字,更無法匹配yyyy-MM-dd HH:mm:ss.SSS,第4個是空字符串,仍然無法匹配。
可以參考下面這樣:
public Gson gson() { String dateFormatWithMS = "yyyy-MM-dd HH:mm:ss.SSS"; String dateFormatNoMS = "yyyy-MM-dd HH:mm:ss"; GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(Date.class, (JsonDeserializer<Date>) (json, typeOfT, context) -> { if (json == null || json.toString().equalsIgnoreCase("\"\"")) { //空字符判斷 return null; } JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive(); SimpleDateFormat sdfMS = new SimpleDateFormat(dateFormatWithMS); SimpleDateFormat sdfNoMS = new SimpleDateFormat(dateFormatNoMS); Date dt = null; try { if (jsonPrimitive.isString()) { if (jsonPrimitive.getAsString().length() == 19) { //這里只是示例,簡單用長度來判斷是哪種格式 //yyyy-MM-dd HH:mm:ss格式 dt = sdfNoMS.parse(json.getAsString()); } else { //yyyy-MM-dd HH:mm:ss.SSS格式 dt = sdfMS.parse(json.getAsString()); } } else if (jsonPrimitive.isNumber()) { //兼容timestamp類型 dt = new Date(jsonPrimitive.getAsLong()); } } catch (Exception e) { //錯誤日志記錄,略 e.printStackTrace(); } return dt; }); Gson gson = builder .setDateFormat(dateFormatWithMS) .setPrettyPrinting() .create(); return gson; }
這樣,剛才這4種情況的date解析都能兼容了。
四、字段別名
@SerializedName(value = "int_field", alternate = {"aaa", "bbb"}) private Integer intField;
這樣的話,json字符串中的int_field, aaa, bbb這3種名字,都能映射到intField上。
五、數組解析
@Test public void testParseList() { String json = "[\"A\",\"B\",\"C\"]"; List<String> list1 = gson().fromJson(json, new TypeToken<List<String>>() { }.getType()); System.out.println(list1.size()); }
TypeToken是個很有用的東西,建議大家多研究