一篇Jackson入門教程奉上

前言

現在開發,基本上都離不開JSON格式,也都會涉及到對象與JSON的序列化與反序列化。在Java的世界中,就不得不說fastjson和jackson了,這兩個庫基本上統治了Java世界中的JSON序列化和反序列化操作。前幾天組內評審代碼,發現一個項目中既在使用fastjson,又在使用jackson,出現亂用的行爲,開發人員也不知道誰是誰,反正就是抓起來用。後來經過評估,統一在項目中使用jackson,同時也總結這篇文章,針對jackson給大家掃盲一下。

Maven依賴

對於非Spring Boot項目,要使用Jackson,則需要添加以下三個依賴:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.11.3</version>
</dependency>


<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.11.3</version>
</dependency>


<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.3</version>
</dependency>

對於Spring Boot項目,引入了web依賴,就引入了Jackson:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

上面的三個依賴,分別對應Jackson的三個核心模塊:

  • Streaming:核心包,提供基於”流模式”解析的相關 API,它包括JsonPaser和JsonGenerator。Jackson內部實現正是通過高性能的流模式API的JsonGenerator和JsonParser來生成和解析 json;

  • Annotations:註解包,提供標準註解功能;

  • Databind:數據綁定包, 提供基於”對象綁定” 解析的相關 API(ObjectMapper )和”樹模型” 解析的相關 API(JsonNode);基於”對象綁定” 解析的API和”樹模型”解析的API依賴基於”流模式”解析的API。

實戰演示

所有的學習都需要落實到實戰上面,下面就把我們在實戰中常用的Jackson用戶進行演示。

下面的演示基於以下幾個Bean類,這幾個Bean類的結構如下所示:

+-weatherBean類
+--WeatherResultBean類
+-------WeatherSkInfoBean類
+-------WeatherInfoBean類

WeatherBean類:

@JsonIgnoreProperties(ignoreUnknown = true)
public class WeatherBean {
    @JsonProperty("resultcode")
    private String resultcode;


    @JsonProperty("reason")
    private String reason;


    @JsonProperty("result")
    private WeatherResultBean result;


    @JsonProperty("error_code")
    private int errorCode;


    @Override
    public String toString() {
        return "resultcode:" + this.resultcode + ";reason:" + this.reason +
                ";error_code:" + this.errorCode;
    }


    public String getResultcode() {
        return resultcode;
    }


    public void setResultcode(String resultcode) {
        this.resultcode = resultcode;
    }


    public String getReason() {
        return reason;
    }


    public void setReason(String reason) {
        this.reason = reason;
    }


    public WeatherResultBean getResult() {
        return result;
    }


    public void setResult(WeatherResultBean result) {
        this.result = result;
    }


    public int getErrorCode() {
        return errorCode;
    }


    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }
}

WeatherResultBean類:

@JsonIgnoreProperties(ignoreUnknown = true)
public class WeatherResultBean {
    @JsonProperty("sk")
    private WeatherSkInfoBean weatherSkInfoBean;


    @JsonProperty("today")
    private WeatherInfoBean weatherInfoBean;


    public WeatherInfoBean getWeatherInfoBean() {
        return weatherInfoBean;
    }


    public void setWeatherInfoBean(WeatherInfoBean weatherInfoBean) {
        this.weatherInfoBean = weatherInfoBean;
    }


    public WeatherSkInfoBean getWeatherSkInfoBean() {
        return weatherSkInfoBean;
    }


    public void setWeatherSkInfoBean(WeatherSkInfoBean weatherSkInfoBean) {
        this.weatherSkInfoBean = weatherSkInfoBean;
    }
}

WeatherSkInfoBean類:

@JsonIgnoreProperties(ignoreUnknown = true)
public class WeatherSkInfoBean {
    @JsonProperty("temp")
    private String temp;


    @JsonProperty("wind_direction")
    private String windDirection;


    @JsonProperty("humidity")
    private String humidity;


    @JsonProperty("time")
    private String time;


    public String getTemp() {
        return temp;
    }


    public void setTemp(String temp) {
        this.temp = temp;
    }


    public String getWindDirection() {
        return windDirection;
    }


    public void setWindDirection(String windDirection) {
        this.windDirection = windDirection;
    }


    public String getHumidity() {
        return humidity;
    }


    public void setHumidity(String humidity) {
        this.humidity = humidity;
    }


    public String getTime() {
        return time;
    }


    public void setTime(String time) {
        this.time = time;
    }
}

WeatherInfoBean類:

@JsonIgnoreProperties(ignoreUnknown = true)
public class WeatherInfoBean {
    @JsonProperty("temperature")
    private String temperature;


    @JsonProperty("weather")
    private String weather;


    @JsonProperty("weather_id")
    private Map<String, String> weatherId;


    @JsonProperty("wind")
    private String wind;


    @JsonProperty("week")
    private String week;


    @JsonProperty("city")
    private String city;


    @JsonProperty("date_y")
    private String currDate;


    @JsonProperty("dressing_index")
    private String dressingIndex;


    @JsonProperty("dressing_advice")
    private String dressingAdvice;


    @JsonProperty("uv_index")
    private String uvIndex;


    @JsonProperty("comfort_index")
    private String comfortIndex;


    @JsonProperty("wash_index")
    private String washIndex;


    @JsonProperty("travel_index")
    private String travelIndex;


    @JsonProperty("exercise_index")
    private String exerciseIndex;


    @JsonProperty("drying_index")
    private String dryingIndex;


    // 省略setter和getter代碼
}

這是一個相對複雜的結構,我也是把主要貼出來了,爭取給大家一個完整的體驗。下面就結合我們實際常用的幾種場景開始總結。

Bean轉JSON

後端開發的接口在給前端返回數據時,都是要求返回JSON串,所以,我們在返回時,都會涉及Bean轉JSON的操作,這個在我們的後端開發中肯定會遇到的。

對於上面的結構,我們通過構建Bean類結構,然後調用ObjectMapperwriteValueAsString方法即可完成轉換。代碼如下:

ObjectMapper mapper = new ObjectMapper();
WeatherInfoBean weatherInfoBean = new WeatherInfoBean();
WeatherSkInfoBean weatherSkInfoBean = new WeatherSkInfoBean();
WeatherResultBean weatherResultBean = new WeatherResultBean();
WeatherBean weatherBean = new WeatherBean();


weatherInfoBean.setCity("呼和浩特");
weatherInfoBean.setComfortIndex("");
weatherInfoBean.setCurrDate("2020年11月1日");
weatherInfoBean.setDressingAdvice("建議着厚外套加毛衣等服裝。年老體弱者宜着大衣、呢外套加羊毛衫。");
weatherInfoBean.setDressingIndex("較冷");
weatherInfoBean.setDryingIndex("");
weatherInfoBean.setExerciseIndex("不適宜");
weatherInfoBean.setTemperature("-3℃~10℃");
weatherInfoBean.setTravelIndex("不適宜");
weatherInfoBean.setUvIndex("高等");
weatherInfoBean.setWashIndex("不適宜");
weatherInfoBean.setWeather("晴轉陰");


Map<String, String> weatherId = new HashMap<>();
weatherId.put("fa", "00");
weatherId.put("fb", "00");
weatherInfoBean.setWeatherId(weatherId);
weatherInfoBean.setWeek("星期日");
weatherInfoBean.setWind("西北風3-5級");


weatherSkInfoBean.setTemp("2");
weatherSkInfoBean.setWindDirection("西南風");
weatherSkInfoBean.setHumidity("39%");
weatherSkInfoBean.setTime("23:20");


weatherResultBean.setWeatherSkInfoBean(weatherSkInfoBean);
weatherResultBean.setWeatherInfoBean(weatherInfoBean);
weatherBean.setResultcode("200");
weatherBean.setReason("請求成功");
weatherBean.setResult(weatherResultBean);
weatherBean.setErrorCode(0);


String json = mapper.writeValueAsString(weatherBean);
System.out.println(json);

JSON轉Bean

在客戶端請求後端的接口時,入參基本都是JSON格式的數據,這個時候就需要我們將JSON格式數據轉成Bean,以便我們取數據進行對應的處理。現在假設客戶端傳過來的數據是這樣的:

{"resultcode":"200","reason":"successed!","result":{"sk":{"temp":"1","wind_direction":"西北風","wind_strength":"2級","humidity":"38%","time":"23:20"},"today":{"temperature":"-3℃~10℃","weather":"晴","weather_id":{"fa":"00","fb":"00"},"wind":"西北風3-5級","week":"星期六","city":"呼和浩特","date_y":"2020年10月31日","dressing_index":"較冷","dressing_advice":"建議着厚外套加毛衣等服裝。年老體弱者宜着大衣、呢外套加羊毛衫。","uv_index":"中等","comfort_index":"","wash_index":"較適宜","travel_index":"較不宜","exercise_index":"較不宜","drying_index":""}},"error_code":0}

對於Jackson來說,我們只需要以下這樣幾行代碼就可以搞定:

ObjectMapper mapper = new ObjectMapper();
String strJson = "{\"resultcode\":\"200\",\"reason\":\"successed!\"," +
        "\"result\":{\"sk\":{\"temp\":\"1\",\"wind_direction\":\"西北風\",\"wind_strength\":\"2級\",\"humidity\":\"38%\",\"time\":\"23:20\"},\"today\":{\"temperature\":\"-3℃~10℃\",\"weather\":\"晴\",\"weather_id\":{\"fa\":\"00\",\"fb\":\"00\"},\"wind\":\"西北風3-5級\",\"week\":\"星期六\",\"city\":\"呼和浩特\",\"date_y\":\"2020年10月31日\",\"dressing_index\":\"較冷\",\"dressing_advice\":\"建議着厚外套加毛衣等服裝。年老體弱者宜着大衣、呢外套加羊毛衫。\",\"uv_index\":\"中等\",\"comfort_index\":\"\",\"wash_index\":\"較適宜\",\"travel_index\":\"較不宜\",\"exercise_index\":\"較不宜\",\"drying_index\":\"\"}},\"error_code\":0}";


Map<String, Object> result = mapper.readValue(strJson, HashMap.class);
WeatherBean weatherBean = mapper.readValue(strJson,
        WeatherBean.class);

Map轉JSON

上面都是通過Bean來和JSON互轉,後來開發不喜歡構建Bean類了,覺的構建Bean類太麻煩,直接通過Map和List來搞定一切,但是在我的團隊內部,我是不建議直接使用Map和List來搞定一切的,這樣對於後期的運維不是很友好,但是,在一些簡單結構的JSON情況下,還是直接使用Map來搞定是比較方便的。

對於上面同樣的結構,我們使用Map來實現一下。

Map<String, Object> weatherSkInfoMap = new HashMap<>();
Map<String, Object> weatherInfoMap = new HashMap<>();
Map<String, Object> weatherResultMap = new HashMap<>();
Map<String, Object> weatherMap = new HashMap<>();


weatherSkInfoMap.put("temp", "1");
weatherSkInfoMap.put("wind_direction", "西北風");
weatherSkInfoMap.put("humidity", "39%");
weatherSkInfoMap.put("time", "23:20");


weatherInfoMap.put("temperature", "-3℃~10℃");
weatherInfoMap.put("weather", "晴");
Map<String, String> weatherIdMap = new HashMap<>();
weatherIdMap.put("fa", "00");
weatherIdMap.put("fb", "00");
weatherInfoMap.put("weather_id", weatherIdMap);
weatherInfoMap.put("wind", "西北風3-5級");
weatherInfoMap.put("week", "星期日");
weatherInfoMap.put("city", "呼和浩特");
weatherInfoMap.put("date_y", "2020年11月1日");
weatherInfoMap.put("dressing_index", "較冷");


weatherResultMap.put("sk", weatherSkInfoMap);
weatherResultMap.put("today", weatherInfoMap);


weatherMap.put("resultcode", "200");
weatherMap.put("reason", "請求成功");
weatherMap.put("result", weatherResultMap);
weatherMap.put("error_code", 0);


String json = mapper.writeValueAsString(weatherMap);
System.out.println(json);

JSON轉Map

同樣,也就存在JSON直接轉成Map的情況,也是很簡單:

ObjectMapper mapper = new ObjectMapper();
String strJson = "{\"resultcode\":\"200\",\"reason\":\"successed!\"," +
        "\"result\":{\"sk\":{\"temp\":\"1\",\"wind_direction\":\"西北風\",\"wind_strength\":\"2級\",\"humidity\":\"38%\",\"time\":\"23:20\"},\"today\":{\"temperature\":\"-3℃~10℃\",\"weather\":\"晴\",\"weather_id\":{\"fa\":\"00\",\"fb\":\"00\"},\"wind\":\"西北風3-5級\",\"week\":\"星期六\",\"city\":\"呼和浩特\",\"date_y\":\"2020年10月31日\",\"dressing_index\":\"較冷\",\"dressing_advice\":\"建議着厚外套加毛衣等服裝。年老體弱者宜着大衣、呢外套加羊毛衫。\",\"uv_index\":\"中等\",\"comfort_index\":\"\",\"wash_index\":\"較適宜\",\"travel_index\":\"較不宜\",\"exercise_index\":\"較不宜\",\"drying_index\":\"\"}},\"error_code\":0}";


MapType javaType =
        mapper.getTypeFactory().constructMapType(HashMap.class,
                String.class, Object.class);
Map<String, Object> result = mapper.readValue(strJson, javaType);
System.out.println(result.toString());

List轉JSON

對於List直接轉JSON的情況,和上面的Map方式類似,示例代碼如下:

List<String> list = new ArrayList<>();
list.add("呼和浩特");
list.add("深圳");
list.add("大連");
list.add("北京");


String json = mapper.writeValueAsString(list);
System.out.println(json);

JSON轉List

對於JSON直接轉List的情況,和上面的Map方式類似,示例代碼如下:

String json = "[\"呼和浩特\",\"深圳\",\"大連\",\"北京\"]";
CollectionType javaType = mapper.getTypeFactory()
        .constructCollectionType(List.class, String.class);
List<String> cityLst = mapper.readValue(json, javaType);
System.out.println(cityLst);

註解說明

在使用Bean的情況下(也是我經常用的情況),我們經常需要配合註解一起使用,這就需要我們對Jackson中的註解有所瞭解。Jackson中的註解有很多,但是我們常用的也就那麼幾個,我們最這幾個常用的重點註解進行一番總結。

註解用法
@JsonProperty用於屬性,把屬性的名稱序列化時轉換爲另外一個名稱
@JsonFormat用於屬性或者方法,把屬性的格式序列化時轉換成指定的格式。示例:@JsonFormat(timezone = “GMT+8”, pattern = “yyyy-MM-dd HH:mm”) private Date date;
@JsonAnySetter用來在序列化和反序列化的時候多餘字段可以通過Map來回轉換。也就是JSON中的字段比對應的JavaBean中的字段多,可以在JavaBean中使用一個Map字段來接收多餘的JSON字段
@JsonAnyGetter同上
@JsonIgnore可用於字段、getter/setter、構造函數參數上,作用相同,都會對相應的字段產生影響。使相應字段不參與序列化和反序列化
@JsonIgnoreProperties(ignoreUnknown = true)該註解是類註解。該註解在Java類和JSON不完全匹配的時候使用。在從JSON反序列化爲Java類的時候,@JsonIgnoreProperties(ignoreUnknown=true)會忽略所有沒有Getter和Setter的屬性,也就是忽略類中不存在的字段
@JsonIgnoreProperties({"internalId", "secretKey"})該註解是類註解。該註解在Java類和JSON不完全匹配的時候使用。在序列化爲JSON的時候,@JsonIgnoreProperties({"internalId", "secretKey"})會忽略internalId和secretKey兩個屬性
@JsonRootName類註解。用於指定JSON根屬性的名稱
@JsonInclude用於定義在序列化時是否不應包含某些“非值”(null值或空值)的註解。可以用於每個字段上,也可以用於類上(表示用於類的所有屬性)。@JsonInclude(value = JsonInclude.Include.NON_NULL)表示忽略類中值爲null的字段;@JsonInclude(Include.NON_EMPTY)忽略類中值爲空的字段,對於字符串,即忽略null或空字符串

總結

希望我的這篇文章對你有幫助。


人生是個圓,有的人走了一輩子也沒有走出命運畫出的圓圈,其實,圓上的每一個點都有一條騰飛的切線。

玩代碼、玩技術

長按識別二維碼,關注“果凍想”

如果覺得還不錯,可以點個“在看”哦~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章