Home jackson使用说明
Post
Cancel

jackson使用说明

引入

首先说到jar包的引入,jackson分为三种jar包

  • core
  • annotation
  • databind

由于databind依赖了core和annotation,所以只需要引入databind即可

demo

一个Pojo类

1
2
3
4
5
6
7
8
9
10
11
@Data
public class APojo {
    private int stuId;
    private String stuName;
    private Date date;
    private BPojo bPojo;
    private List<Integer> intList;
    private Map<Integer, String> map;
    private int[] intArr;
    private AEnum aEnum;
}

这里大致覆盖了大部分的java类型,额外的类型或者千层饼嵌套类型可以自己尝试

使用Objectapper变换jsonString和object

1
2
3
4
5
6
APojo li = new APojo(1, "li", new Date(), new BPojo("zxcdv", 2), list, map, new int[]{1, 2}, AEnum.AA);

ObjectMapper mapper = new ObjectMapper();
String s = mapper.writeValueAsString(li);

APojo aPojo = mapper.readValue(s, APojo.class);

一个write一个read就可以覆盖80%的使用场景了,所以这里不是重点,重点是序列化后的格式如何?

Tips (IMPORTANT):
需要注意,使用反序列化必须有无参的构造函数,所以有@AllArgsConstructor那也必须要配对一个@NoArgsConstructor,否则反序列化时直接报错

默认序列化格式

对于上面的对象序列化的结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
    "stuId":1,
    "stuName":"li",
    "date":1595515913865,
    "intList":[
        56,
        5
    ],
    "map":{
        "1":"asdf",
        "3":"zxcb"
    },
    "intArr":[
        1,
        2
    ],
    "aenum":"AA",
    "bpojo":{
        "a1":"zxcdv",
        "b2":2
    }
}

需要注意的一些点:

  • Date会被转换成时间戳
  • List和array都是一样地用[item1, item2…]来表示
  • map和object一样都是用{“key”: value, …}来表示
  • enum的值是枚举的名称,而不是内部值(这里的内部值是一个int)

最需要注意的一点是字段名称的转换
stuId -> stuId
bPojo -> bpojo
看到这个转换规则是不是很懵逼?
其实逻辑就是如果变量名最前面只有至多一个小写字母,那么最前面的所有大写字母都会变成小写,例如:
aAAdd -> aaadd
AAAdd -> aaadd
aaAdd -> aaAdd
AAaDD -> aaaDD

所以,如果不是全小写还是推荐使用@JsonProperty()注解来指定序列化的名称

有趣的是在gradle中使用@AllargsConstructor,然后使用全参的构造函数会报错,可能缺少什么配置,后续跟进

常用的jackson annotation

@JsonProperty

这个应该是最常用的注解了,因为他涵盖了序列化名称是否必须使用位置的索引默认值访问权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
String value() default "";

boolean required() default false;

int index() default -1;

String defaultValue() default "";

JsonProperty.Access access() default JsonProperty.Access.AUTO;

public static enum Access {
    AUTO,
    READ_ONLY,
    WRITE_ONLY,
    READ_WRITE;

    private Access() {
    }
}

序列化名称就是json中字段的名称,如果必须那么json反序列化时必须提供该字段否则报错
位置的索引则是字段在json中的位置(相对大小即可,无需一一紧靠,索引值越小排在越前面)
默认值(预备字段,现在还不能使用)
访问权限可以设置读、写、读写(反序列化,序列化,双向)

@JsonIgnoreProperties

这个可以指定需要忽略的字段,但是最常用的还是@JsonIgnoreProperties(ignoreUnknown = true),如果不使用这个,那么如果json中包括pojo中不存在的字段,则反序列化时会直接报错。
当然即便使用了,如果传入的字段的值属性有误,也会报错。

其余的我们按照读写的使用区别来分类讨论

读写

@JsonIgnore

直接在字段上使用,读写时都会忽略,不会写到json中,而生成obj时则是默认值。

@JsonIgnoreProperties

上面说了其最主要的使用方法,还可以在类上使用来一一指定需要忽略的字段@JsonIgnoreProperties({“firstName”,“lastName”})

@JsonIgnoreType

直接在类上使用,整个类都不会序列化和反序列化,例如我们将这个注释写在demo中的BPojo上,序列化的结果就没有bPojo

@JsonAutoDetect

这个属实没啥用,这个的使用场景是在没有getter、setter方法时,使用这个注解来自动侦测字段 例如:

1
2
3
4
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NON_PRIVATE)
public class APojo {
    private int stuId;
    Date date;

首先这个pojo没有setter和getter,然后有一个private和default的字段,我们这时候将filed可见性调整至非private,这样date就可以被序列化和反序列化 。。。多此一举
当然有多种类型可以自由组合:
可见性: fieldVisibility,getterVisibility,isGetterVisibility,setterVisibility,creatorVisibility
字段可见性:ANY, NON_PRIVATE, PROTECTED_AND_PUBLIC, PUBLIC_ONLY, NONE, DEFAULT;

@JsonSetter

在json字段和java字段名不同的时候使用,作用在setter方法上,例如json为id,java为stuId,那我们可以:

1
2
3
4
@JsonSetter("id")
public void setStuId(int stuId) {
    this.stuId = stuId;
}

当然,我想不明白为什么不使用@JsonProperty

@JsonAnySetter

这个可能稍微有点用,我们之前说过,可能会传入不属于java的字段,我们可以使用@JsonIgnoreProperties来忽略他们,当然如果你需要记录一下这些额外字段时,你可以用这个注解配合一个map来接收这些kv

1
2
3
4
5
6
7
8
9
10
11
@JsonIgnore
private Map<String, Object> properties = new HashMap<>();

@JsonAnySetter
public void set(String fieldName, Object value){
    this.properties.put(fieldName, value);
}

public Object get(String fieldName){
    return this.properties.get(fieldName);
}

创建一个map,设置一个setter方法,但是这个setter方法并不是设置properties的引用,而是put kv,并在这个set方法上使用@JsonAnySetter 其次,这个map我们也不会传出去,所以使用@JsonIgnore去忽略他,有趣的是这难道不会在反序列化的时候无法写入吗? @JsonIgnore会让这个properties在反序列化时无法赋值,但是@JsonAnySetter指定的set方法其实是put方法,所以可以写入。

@JsonCreator

没有setter或者无参构造函数时,你可以在构造方法上使用这个注解,反序列化时就可以使用有参的构造器来构建obj,但是需要指定每一个字段的json名,否则会报错

1
2
3
4
@JsonCreator
public PersonImmutable(
        @JsonProperty("id")  long id,
        @JsonProperty("name") String name) {

@JsonDeserialize

使用自定义的反序列化器,需要指定class,并且该类需要实现一个接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class PersonDeserialize {
 
    public long    id      = 0;
    public String  name    = null;
 
    @JsonDeserialize(using = OptimizedBooleanDeserializer.class)
    public boolean enabled = false;
}


public class OptimizedBooleanDeserializer extends JsonDeserializer<Boolean> {
 
    @Override
    public Boolean deserialize(JsonParser jsonParser,
            DeserializationContext deserializationContext) throws
        IOException, JsonProcessingException {
 
        String text = jsonParser.getText();
        if("0".equals(text)) return false;
        return true;
    }
}

其中泛型指定的是返回值(java字段中的),而jsonParser.getText()读取的是json的string value

介绍完了读相关的注解,写的部分注解即为读注解的逆操作,可以对照学习

@JsonInclude

这个指定了在何种情况下的字段才会导出

1
2
3
4
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class PersonInclude {
    public long  personId = 0;
    public String name     = null;

例如指定了non_empty并且name为null的话,name就不会导出到json中。

@JsonGetter

这个就是@JsonSetter的孪生兄弟,作用在Getter方法上,导出到json中的字段为注解指定的字段。

@JsonAnyGetter

@JsonAnySetter是使用一个map接收所有json中unknown的字段,而@JsonAnyGetter则是摊平(flat)map中的字段

1
2
3
4
5
private Map<Integer, String> map;
    @JsonAnyGetter
    public Map gettttt() {
        return map;
    }

假如map中包括 {a:1, b:2}
那么map中的元素也会和其他元素平级出现在json中,不过最好别用,万一重名咋整
还有就是如果使用了@JsonAnyGetter那么必须使用@JsonAnySetter,否则序列化和反序列化是不可逆的,除非他们再重新拼出一个map传入

@JsonPropertyOrder

这个顾名思义是指定json中的字段顺序的,但是和@JsonProperty使用index不同,这个需要写在类上,并且显式声明字段的顺序

1
2
3
4
5
@JsonPropertyOrder({"name", "personId"})
public class PersonPropertyOrder {
    public long  personId  = 0;
    public String name     = null;
}

这样,那么就会出现在最前面

@JsonRawValue

直接输出原始值,有点像sql的${}
重点使用场景就是如果一个字段String表示的是一段json文本,并且需要输出到json作为一个可读的json obj而不是一串字符串时使用。

1
2
3
4
5
6
public class PersonRawValue {
    public long   personId = 0;
    @JsonRawValue
    public String address  =
            "{ \"street\" : \"Wall Street\", \"no\":1}";
}

这样,序列化后就会直接生成一个json子对象

1
{"personId":0,"address":{ "street" : "Wall Street", "no":1}}

@JsonValue

这个非常奇怪,调用序列化方法会生成一个自定义的字符串,类似toString方法

1
2
3
4
@JsonValue
public String toJson() {
    return stuName + " de id is:" + stuId;
}

之后我们调用writeAsString方法就会返回这个toJson的值,相当于我们要在这个方法中自行完成json字符串的拼接。。。
那我用你这个jackson干嘛。。。

@JsonSerialize

之前介绍了custom反序列化器,现在则是custom序列化器

1
2
3
4
5
6
public class PersonSerializer {
    public long   personId = 0;
    public String name     = "John";
    @JsonSerialize(using = OptimizedBooleanSerializer.class)
    public boolean enabled = false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class OptimizedBooleanSerializer extends JsonSerializer<Boolean> {
 
    @Override
    public void serialize(Boolean aBoolean, JsonGenerator jsonGenerator, 
        SerializerProvider serializerProvider) 
    throws IOException, JsonProcessingException {
 
        if(aBoolean){
            jsonGenerator.writeNumber(1);
        } else {
            jsonGenerator.writeNumber(0);
        }
    }
}

尾巴

自此,大部分的jackson注解已经记录完了,其实正常使用时基本上也就只能用到2、3个注解,因为需要额外的功能我们不需要添加注解,我们一般增加、减少java的字段即可。
使用复杂的注解徒增复杂度,也就只有在做适配器的时候才可能使用一些复杂的注解。

This post is licensed under CC BY 4.0 by the author.

在线书库安装介绍

SpringProfile