• Jackson注释的使用


    在这里插入图片描述

    Jackson提供了一系列注解,可以使用这些注解来设置将JSON读入对象的方式或从对象生成什么JSON的方式,下面介绍一些常用的注解。

    3.1 序列化

    @JsonAnyGetter

      @JsonAnyGetter 注解运行可以灵活的使用Map类型的作为属性字段,允许getter方法返回Map,该Map然后用于以与其他属性类似的方式序列化JSON的其他属性。通过序列化该实体Bean,我们将会得到Map属性中的所有Key作为属性值,测试序列化代码如下

    import java.util.HashMap;
    import java.util.Map;
    import com.fasterxml.jackson.annotation.JsonAnyGetter;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class JacksonTester {
    	public static void main(String args[]) throws JsonProcessingException {
    		ExtendableBean bean = new ExtendableBean("My bean");
    		bean.add("attr1", "val1");
    		bean.add("attr2", "val2");
    		String result = new ObjectMapper().writeValueAsString(bean);
    		System.out.println(result);
    	}
    }
    
    class ExtendableBean {
    	public String name;
    	private Map<String, String> properties;
    
    	@JsonAnyGetter
    	public Map<String, String> getProperties() {
    		return properties;
    	}
    
    	public ExtendableBean(String name) {
    		this.name = name;
    		this.properties = new HashMap<String, String>();
    	}
    
    	public void add(String key, String value) {
    		this.properties.put(key, value);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    最终输出结果如下

    {
        "name":"My bean",
        "attr2":"val2",
        "attr1":"val1"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果不使用**@JsonAnyGetter**注解,那么最终序列化结果将会在properties属性下面,结果如下:

    {
        "name": "My bean",
        "properties": {
            "attr2": "val2",
            "attr1": "val1"
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    @JsonRawValue

    @JsonRawValue注解可以指定字符串属性类为json,如下代码:

    import java.io.IOException;
    import java.text.ParseException;
    import com.fasterxml.jackson.annotation.JsonRawValue;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class JacksonTester {
    	public static void main(String args[]) throws IOException, ParseException {
    		RawBean bean = new RawBean("My bean", "{\"attr\":false}");
    		String result = new ObjectMapper().writeValueAsString(bean);
    		System.out.println(result);
    	}
    }
    
    class RawBean {
    	public String name;
    	@JsonRawValue
    	public String json;
    
    	public RawBean(String name, String json) {
    		super();
    		this.name = name;
    		this.json = json;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    最终使用@JsonRawValue序列化结果如下:

    {
        "name":"My bean",
        "json":{
            "attr":false
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    没有使用@JsonRawValue序列化结果如下:

    {
        "name":"My bean",
        "json":"{\"attr\":false}"
    }
    
    • 1
    • 2
    • 3
    • 4

    @JsonRootName

      @JsonRootName注解旨在给当前序列化的实体对象加一层包裹对象,允许在JSON上指定根节点。

    @data
    public class RootUser {
     
        private String name;
        private String title;
     
        public RootUser(String name, String title) {
            this.name = name;
            this.title = title;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在上面的实体类中,正常情况下,如果要序列号RootUser对象,其结果格式为:

    {
        "name": "name1",
        "title": "title1"
    }
    
    • 1
    • 2
    • 3
    • 4

    在RootUser加上**@JsonRootName** 注解后,该类改动如下:

    @JsonRootName(value = "root")
    public class RootUser {
     
        private String name;
        private String title;
     
        public RootUser(String name, String title) {
            this.name = name;
            this.title = title;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

      @JsonRootName 仅用于指定 JSON 根属性的名称,只有当SerializationFeature.WRAP_ROOT_VALUE、DeserializationFeature.UNWRAP_ROOT_VALUE 启用时才有效。若只开启了 Feature 功能但未使用 @JsonRootName 注解,默认会使用类名作为 RootName。

    ObjectMapper objectMapper=new ObjectMapper();
    // 启用 SerializationFeature.WRAP_ROOT_VALUE
    objectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
    // 启用 DeserializationFeature.UNWRAP_ROOT_VALUE
    objectMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
    String result=objectMapper.writeValueAsString(new RootUser("name1","title1"));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    最终序列化JSON结果如下:

    {
        "root": {
            "name": "name1",
            "title": "title1"
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    @JsonSerialize

      实际开发中,一定遇到过这样的问题:前端显示和后台存储数据单位不统一,而且各有各自的理由,统一不了,那就只能由后端转换。每次返回给前端时再转换一遍,返回给前端的json数据,在后端里定义的往往是一个对象,如何做到优雅的转换呢?例如,Double类型的数据返回值转换成字符串,只需两步操作:

    1. 写一个负责转换的类,里面写好规则。
    public class CustomDoubleSerialize extends JsonSerializer<Double> {
    
    	@Override
    	public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    		BigDecimal d = new BigDecimal(value.toString());
    		gen.writeString(d.stripTrailingZeros().toPlainString());
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 在实体类上需要装换的字段上加上注解
    /**
     * 金额
     */
    @JsonSerialize(using = CustomDoubleSerialize.class)
    private Double amount;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    又例如,假设某布尔值false和true,使其分别为0和1。

    1. 写一个负责转换的类,里面写好规则:将序列的真值序列化为1,将假值序列化为0。
    public class OptimizedBooleanSerializer extends JsonSerializer<Boolean> {
     
        @Override
        public void serialize(Boolean aBoolean, JsonGenerator gen, SerializerProvider provider) throws IOException {
            if(aBoolean){
                jsonGenerator.writeNumber(1);
            } else {
                jsonGenerator.writeNumber(0);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 在实体类上需要装换的字段上加上注解
    @JsonSerialize(using = OptimizedBooleanSerializer.class)
    public boolean enabled = false;
    
    • 1
    • 2

    注:@JsonSerialize注解,主要应用于数据转换,该注解作用在该属性的getter方法上

    @JsonGetter

      @JsonGetter 注解用于告诉Jackson,应该通过调用getter方法而不是通过直接字段访问来获取某个字段值。 如果您的Java类使用getter和setter名称,则@JsonGetter注解很有用。

    import com.fasterxml.jackson.annotation.JsonGetter;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class JacksonTester {
    	public static void main(String args[]) throws JsonProcessingException {
    		ObjectMapper mapper = new ObjectMapper();
    		Student student = new Student("Mark", 1);
    		String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(student);
    		System.out.println(jsonString);
    
    	}
    }
    
    class Student {
    	private String name;
    	private int rollNo;
    
    	public Student(String name, int rollNo) {
    		this.name = name;
    		this.rollNo = rollNo;
    	}
    
    	@JsonGetter("studentName")
    	public String getName() {
    		return name;
    	}
    
    	public int getRollNo() {
    		return rollNo;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    使用 @JsonGetter 输出如下结果:

    {
      "rollNo" : 1,
      "studentName" : "Mark"
    }
    
    • 1
    • 2
    • 3
    • 4

    而没有使用 @JsonGetter 输出如下结果:

    {
      "name" : "Mark",
      "rollNo" : 1
    }
    
    • 1
    • 2
    • 3
    • 4

    3.2 反序列化

    @JsonCreator

      @JsonCreator 用于告诉Jackson该Java对象具有一个构造函数,该构造函数可以将JSON对象的字段与Java对象的字段进行匹配。配合@JsonProperty注解能到达在反序列化实体对象时,指定不变更属性名称的效果。例如有如下JSON:

    {
        "id":1,
        "theName":"My bean"
    }
    
    • 1
    • 2
    • 3
    • 4

    在实体类中,我们没有属性名称是theName,但我们想把theName属性反序列化时赋值给name,此时实体类对象结构如下:

    public class BeanWithCreator {
    	public int id;
    	public String name;
    
    	@JsonCreator
    	public BeanWithCreator(@JsonProperty("id") int id, @JsonProperty("theName") String name) {
    		this.id = id;
    		this.name = name;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在BeanWithCreator的构造函数中添加@JsonCreator注解,并且配合@JsonProperty注解进行属性指向,最终反序列化代码如下:

    import java.io.IOException;
    import java.text.ParseException;
    import com.fasterxml.jackson.annotation.JsonCreator;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class JacksonTester {
    	public static void main(String args[]) throws IOException, ParseException {
    		String json = "{\"id\":1,\"theName\":\"My bean\"}";
    
    		BeanWithCreator bean = new ObjectMapper().readerFor(BeanWithCreator.class).readValue(json);
    		System.out.println(new ObjectMapper().writeValueAsString(bean));
    	}
    }
    
    class BeanWithCreator {
    	public int id;
    	public String name;
    
    	@JsonCreator
    	public BeanWithCreator(@JsonProperty("id") int id, @JsonProperty("theName") String name) {
    		this.id = id;
    		this.name = name;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    @JsonAnySetter

      @JsonAnySetter 表示Jackson为JSON对象中所有无法识别的字段调用相同的setter方法, “无法识别”是指尚未映射到Java对象中的属性或设置方法的所有字段。@JsonAnySetter注解可以将来源JSON最终转化为Map类型的属性结构。

    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    import com.fasterxml.jackson.annotation.JsonAnySetter;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class JacksonTester {
    	public static void main(String args[]) {
    		ObjectMapper mapper = new ObjectMapper();
    		String jsonString = "{\"RollNo\" : \"1\",\"Name\" : \"Mark\"}";
    		try {
    			Student student = mapper.readerFor(Student.class).readValue(jsonString);
    			System.out.println(student.getProperties());
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    class Student {
    	private Map<String, String> properties;
    
    	public Student() {
    		properties = new HashMap<>();
    	}
    
    	public Map<String, String> getProperties() {
    		return properties;
    	}
    
    	@JsonAnySetter
    	public void add(String property, String value) {
    		properties.put(property, value);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    @JsonSetter

      @JsonSetter注解是@JsonProperty的替代注解,用于告诉Jackson将特定方法标记为setter方法。当将JSON读入对象时,应将此setter方法的名称与JSON数据中的属性名称匹配。当我们需要读取一些JSON数据时,但是目标实体类与该数据的属性名称不同,该注解是非常有用的。

    import com.fasterxml.jackson.annotation.JsonSetter;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class JacksonTester {
    	public static void main(String args[]) throws JsonProcessingException {
    		String json = "{\"id\":1,\"name\":\"My bean\"}";
    		ObjectMapper mapper = new ObjectMapper();
    		MyBean myBean = mapper.readerFor(MyBean.class).readValue(json);
    		System.out.println(mapper.writeValueAsString(myBean));
    	}
    }
    
    class MyBean {
    	private int id;
    	private String thename;
    
    	/**
    	 * 通过指定setTheName作为属性name的setter方法,反序列化时可以达到最终效果
    	 */
    	@JsonSetter("name")
    	public void setTheName(String name) {
    		this.thename = name;
    	}
    
    	public int getId() {
    		return id;
    	}
    
    	public void setId(int id) {
    		this.id = id;
    	}
    
    	public String getTheName() {
    		return thename;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    @JsonDeserialize

      @JsonDeserialize注解和序列化注解@JsonSerialize的效果是一致的,作用与反序列化时,针对特定的字段,存在差异化的发序列化效果。首先,需要将@JsonDeserialize注解添加到要为其使用自定义反序列化器的字段,如下所示

    public class PersonDeserialize {
     
        public long id = 0;
        public String  name = null;
     
        @JsonDeserialize(using = OptimizedBooleanDeserializer.class)
        public boolean enabled = false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    其次,这是@JsonDeserialize注解中引用的OptimizedBooleanDeserializer类的实例

    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    请注意,OptimizedBooleanDeserializer类使用通用类型Boolean扩展了JsonDeserializer。 这样做会使deserialize()方法返回一个布尔对象。 如果要反序列化其他类型(例如java.util.Date),则必须在泛型括号内指定该类型。

    3.3 属性注解

    @JsonIgnore

      Jackson注解 @JsonIgnore 用来告诉 Jackson 在处理时忽略Java对象的某个属性(字段),在将JSON读取到Java对象中以及将Java对象写入JSON时,都将忽略该属性。

    @Data
    public class SellerInfoEntity {
     
        private String id;
        private String username;
        private String password;
        
        @JsonIgnore
        private Timestamp createTime;
        @JsonIgnore
        private Timestamp updateTime;
     
        public SellerInfoEntity() {
        }
     
        public SellerInfoEntity(String id, String username, String password) {
            this.id = id;
            this.username = username;
            this.password = password;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    使用注解前

    {
        "id":"1",
        "username":"user1",
        "password":"123456",
        "createTime":null,
        "updateTime":null
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用注解后,不会从JSON读取或写入JSON属性(createTime、updateTime)

    {
        "id":"1",
        "username":"user1",
        "password":"123456"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    @JsonIgnoreProperties

      与 @JsonIgnore 类似,都是告诉 Jackson 该忽略哪些属性,不同之处是 @JsonIgnoreProperties 是类级别的,放置在类声明上方,达到在序列化时忽略一个或多个字段的效果。

    @Data
    @JsonIgnoreProperties(value = {"createTime","updateTime"})
    public class SellerInfoEntity {
     
        private String id;
        private String username;
        private String password;
        private String openid;
        private Timestamp createTime;
        private Timestamp updateTime;
     
        public SellerInfoEntity() {
        }
     
        public SellerInfoEntity(String id, String username, String password) {
            this.id = id;
            this.username = username;
            this.password = password;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在此示例中,属性createTime、updateTime都将被忽略,因为它们的名称在类声明上方的 @JsonIgnoreProperties 注解声明内列出。

    在目标对象的类级别上添加注解:@JsonIgnoreProperties(ignoreUnknown = true),用于忽略字段不匹配情况。

    @JsonInclude

    使用@JsonInclude注解告诉Jackson仅在某些情况下包括属性。 例如,仅当属性为非null,非空或具有非默认值时,才应包括该属性。

    @JsonInclude(Include.NON_NULL)
    public class MyBean {
        public int id;
        public String name;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在MyBean中使用了Include.NON_NULL则代表该实体对象序列化时不会包含空值。

    3.4 常规注解

    @JsonProperty

      类似于sql里字段的别名,可以指定某个属性和json映射的名称。当实体对象中没有标准的getter/setter方法时,我们可以使用该注解进行指定属性名称,方便进行序列化/反序列化。例如我们有个json字符串为{"user_name":"aaa"},而java中命名要遵循驼峰规则,则为userName,这时通过 @JsonProperty 注解来指定两者的映射规则即可。

    public class SomeEntity {
        @JsonProperty("user_name")
        private String userName;
          // ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    @JsonFormat

      此注解用于属性上,针对日期字段可以通过使用@JsonFormat注解直接转化为指定的格式,例如 @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss")。主要有下面几个常用的属性

    属性说明
    shap表示序列化后的一种类型
    pattern表示日期的格式
    timezone若未标明时区,则默认为 GMT 时区,中国需要GMT+8
    locale根据位置序列化的一种格式
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy年MM月dd日 HH:mm:ss", timezone = "GMT+8")
    private Date date;
    
    @JsonFormat(shape = JsonFormat.Shape.NUMBER)
    private Date date2;
    
    @JsonFormat(locale = "zh_CN")
    private Date date3;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    跟 JsonSerializer 类似,如下所示:

    @JsonSerialize(using = CustomDateSerializer.class) 
    private Date dateOfBirth; 
    
    class CustomDateSerializer extends JsonSerializer{
       private static final long serialVersionUID = 1L; 
       private static SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
       @Override 
       public void serialize(Date value, 
          JsonGenerator generator, SerializerProvider arg2) throws IOException { 
          generator.writeString(formatter.format(value)); 
       } 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    哈夫曼树与哈夫曼编码
    界面组件Kendo UI for React R3 2022新版,让Web应用更酷炫
    Spring事务
    WinUI(WASDK)使用MediaPipe检查人体姿态关键点
    Dos攻击与DDos攻击
    Python武器库开发-flask篇之路由和视图函数(二十二)
    7天学完Spring:基础学习结束,关于Spring事务及其传播机制
    设计模式之发布订阅、观察者模式
    chromium源码的下载与编译
    Go语言学习笔记——Golang 1.18新特性泛型
  • 原文地址:https://blog.csdn.net/duleilewuhen/article/details/127760745