Jackson

Jackson主要包含了3个模块:jackson-core、jackson-annotations、jackson-databind。有三种方式处理Json:1、使用底层的基于Stream的方式对Json的每一个小的组成部分进行控制。2、使用Tree Model,通过JsonNode处理单个Json节点。3、使用databind模块,直接对Java对象进行序列化和反序列化。通常来说,我们在日常开发中使用的是第3种方式。本文也是介绍第3种方式的序列化和反序列化。

依赖

<!--jackson-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

下面例子中所使用的对象:

@Data
@Accessors(chain = true)
public class EmployeeVo implements Serializable {
    private Long id;
    private String name;
    private Short sex;
    private Date birthday;
    private String telephone;
    private BigDecimal salary;
    private Boolean isOfficial;
    private String hobby;

    public String getAbc() {
        return "abc方法";
    }
}

默认配置的对象序列化(ObjectMapper.writeValueAsString)

    @Test
    public void writeValueAsString() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        EmployeeVo employeeVo = new EmployeeVo();
        employeeVo.setId(1000L)
                .setName("haha")
                .setBirthday(new Date())
                .setSalary(new BigDecimal("19865.32"))
                .setIsOfficial(true)
                .setSex(new Short("0"))
                .setTelephone("021-2546589");
        System.out.println(objectMapper.writeValueAsString(employeeVo));
    }

输出结果:

{"id":1000,"name":"haha","sex":0,"birthday":1592019533928,"telephone":"021-2546589","salary":19865.32,"isOfficial":true,"hobby":null,"abc":"abc方法"}

说明:

  1. 空值会输出null
  2. Date类型会输出时间戳
  3. 序列化依赖于getter方法,如果某个字段没有getter方法,那么该字段是不会被序列化的
  4. 通过getter的命名规约进行调用,若有getAbc()方法,没有abc属性则也会序列化输出"abc":""

默认配置的对象反序列化(ObjectMapper.readValue)

@Test
public void readValue() throws JsonProcessingException {
    String jsonStr = "{\"id\":1000,\"name\":\"haha\",\"sex\":0,\"birthday\":1592013569703,\"salary\":19865.32,"isOfficial":true}";
    ObjectMapper objectMapper = new ObjectMapper();
    EmployeeVo employeeVo = objectMapper.readValue(jsonStr, EmployeeVo.class);
    System.out.println(employeeVo);
}

输入出结果:

EmployeeVo(id=1000, name=haha, sex=0, birthday=Sat Jun 13 09:59:29 CST 2020, telephone=null, salary=19865.32, isOfficial=null, hobby=null)

说明:

  1. 时间戳可以反序列化成Date
  2. Long、String、Short、BigDecimal、Boolean类型默认都支持

常用配置


反序列化时忽略对象中不存在的json字段

假如对JSON字符串做反序列化时,JSON存在对象中没有属性,默认反序列化报错。如多了一个extra属性。

String jsonStr = "{\"id\":1000,\"name\":\"haha\",\"sex\":0,\"birthday\":1592013569703,\"salary\":19865.32,\"extra\":\"test123\"}";

报如下错误:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "extra" (class com.zmx.model.vo.EmployeeVo), not marked as ignorable (8 known properties: "hobby", "telephone", "isOfficial", "sex", "salary", "id", "birthday", "name"])

解决办法:

  1. 在EmployeeVo中添加extra属性(不可取)
  2. objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  1. @JsonIgnoreProperties(ignoreUnknown = true) 用于类上,对于单个对象生效,而对于JSON字符串中存在的未知字段,在反序列化时忽略。
@JsonIgnoreProperties(ignoreUnknown = true)
public class EmployeeVo implements Serializable {
}
忽略字段

在序列化和反序列化时可以选择性让某些属性忽略,不会序列化也不会反序列化。

  1. @JsonIgnore 用于字段上,表示该字段在序列化和反序列化的时候都将被忽略。
@JsonIgnore
private String hobby;
  1. @JsonIgnoreProperties 主要用于类上。下面表示sex与birthday,反序列化和序列化均忽略
@JsonIgnoreProperties(value = {"sex", "birthday"})
null值属性序列化时优化处理

默认情况下序列化时,对NULL属性出成的JSON字符串会生成:"属性名":null。通过以下设置可以设置成不输出空值属性。

  1. @JsonInclude(JsonInclude.Include.NON_NULL) 设置在类上面,对单个类生效。
    @JsonInclude(JsonInclude.Include.NON_EMPTY),表示null和集合为空, @JsonInclude(JsonInclude.Include.NON_DEFAULT),表示为赋初值的情况(null,0,0.0,空集合,false);

  2. objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 设置在objectMapper上面,对objectMapper全局生效。

Date序列化和反序列化
  1. @JsonFormat (com.fasterxml.jackson.annotation.JsonFormat) 做用在类属性上面。
@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss",timezone = "GMT+8")
private Date birthday;
  1. 设置ObjectMapper.setDateFormat()
//去掉默认的时间戳格式
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//设置为东八区
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
//序列化时,日期的统一格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
对没有get,set方法的对象序列化和反序列化
    @Test
    public void writeValueAsString2() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
        objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
        EmployeeVo employeeVo = new EmployeeVo();
        employeeVo.setId(1000L).setName("haha").setBirthday(new Date()).setSalary(new BigDecimal("19865.32")).setIsOfficial(true).setSex(new Short("0"));
        System.out.println(objectMapper.writeValueAsString(employeeVo));
    }

结果:

{"id":1000,"name":"haha","sex":0,"birthday":1592029403004,"telephone":null,"salary":19865.32,"isOfficial":true,"hobby":null}
自己定义解析器

自定义序列化解析器

public class BigDecimalSerializer extends JsonSerializer<BigDecimal> {
    @Override
    public void serialize(BigDecimal bigDecimal, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        DecimalFormat decimalFormat = new DecimalFormat("#,##0.00");
        jsonGenerator.writeString(decimalFormat.format(bigDecimal));

    }
}

自定义反序列化解析器

public class BigDecimalDeSerializer extends JsonDeserializer<BigDecimal> {

    @Override
    public BigDecimal deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        DecimalFormat decimalFormat = new DecimalFormat("#,##0.00");
        Number value = BigDecimal.ZERO;
        try {
            value = decimalFormat.parse(jsonParser.getText());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return new BigDecimal(value.longValue());
    }
}
  1. 做用于局部类上面
@JsonSerialize(using = BigDecimalSerializer.class)
@JsonDeserialize(using = BigDecimalDeSerializer.class)
protected BigDecimal salary;
  1. 做用于全局
ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer(BigDecimal.class, new BigDecimalSerializer());
        module.addDeserializer(BigDecimal.class, new BigDecimalDeSerializer());
        objectMapper.registerModule(module);
其它配置
  1. @JsonIgnoreType 标注在类上,当该类作为其他类的属性时,该属性将被忽略。
  2. @JsonProperty 可以指定某个属性和json映射的名称。例如我们有个json字符串为{“user_name”:”aaa”},而java中命名要遵循驼峰规则,则为userName,这时通过@JsonProperty 注解来指定两者的映射规则即可。这个注解也比较常用。
  3. @JsonSetter 标注于 setter 方法上,类似 @JsonProperty ,也可以解决 json 键名称和 java pojo 字段名称不匹配的问题。

常用的ObjectMapper的设置

ObjectMapper objectMapper = new ObjectMapper();
//去掉默认的时间戳格式     
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//设置为东八区
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
// 设置输入:禁止把POJO中值为null的字段映射到json字符串中
objectMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
 //空值不序列化
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//反序列化时,属性不存在的兼容处理
objectMapper.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
//序列化时,日期的统一格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//序列化日期时以timestamps输出,默认true
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//序列化枚举是以toString()来输出,默认false,即默认以name()来输出
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);
//序列化枚举是以ordinal()来输出,默认false
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,false);
//类为空时,不要抛异常
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
//反序列化时,遇到未知属性时是否引起结果失败
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
 //单引号处理
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
//解析器支持解析结束符
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);

参考于:
https://www.jianshu.com/p/af96d8a5769d
https://www.jianshu.com/p/67b6da565f81

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352