一篇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或空字符串

总结

希望我的这篇文章对你有帮助。


人生是个圆,有的人走了一辈子也没有走出命运画出的圆圈,其实,圆上的每一个点都有一条腾飞的切线。

玩代码、玩技术

长按识别二维码,关注“果冻想”

如果觉得还不错,可以点个“在看”哦~

posted @ 2020-11-07 20:03  ^_^果冻^_^  阅读(6)  评论(0编辑  收藏  举报  来源