Jackson教程
字段命名之JsonAlias、JsonGetter和JsonSetter

序列化时如果需要修改字段名,比较常见的是使用JsonProperty注解来实现。 有时候,序列化和反序列化需要使用不同的字段名称。在《Jackson修改字段名和自定义命名策略》一文中讲到,使用自定义的命名策略,可以分别指定getter和setter的方法名称,从而实现序列化和反序列化同一字段时采用不同的JSON属性名称。 除此之外,Jackson还提供了几个注解,可以实现类似的功能。这几个注解,分别是JsonAlias、JsonGetter和JsonSetter。 本篇内容基于Jackson 2.11.2版本,马上开始学习吧。

JsonAlias指定反序列化别名

JsonAlias注解,支持指定多个别名。该注解可用于字段、getter和setter方法,效果是一样的。

JsonAlias反序列化

    public class Article {
    
        private String title;
        @JsonAlias({"cont", "detail"})
        private String content;
    
        // 省略getter、setter方法
    }
    
 
    /**
     * 反序列化时,既支持使用原来的字段名,也支持使用指定的任一别名
     * 
     * @throws JsonMappingException
     * @throws JsonProcessingException
     */
    @Test
    public void deserializeWithJsonAlias() throws JsonMappingException, JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
    
        // 使用原字段名
        String jsonString = "{\"title\":\"spring\",\"content\":\"flowers\"}";
        Article article = mapper.readValue(jsonString, Article.class);
        System.out.println(mapper.writeValueAsString(article));
    
        // 使用别名cont
        jsonString = "{\"title\":\"spring\",\"cont\":\"flowers\"}";
        article = mapper.readValue(jsonString, Article.class);
        System.out.println(mapper.writeValueAsString(article));
    
        // 使用别名detail
        jsonString = "{\"title\":\"spring\",\"detail\":\"flowers\"}";
        article = mapper.readValue(jsonString, Article.class);
        System.out.println(mapper.writeValueAsString(article));
    }

执行结果:

    {"title":"spring","content":"flowers"}
    {"title":"spring","content":"flowers"}
    {"title":"spring","content":"flowers"}

使用JsonAlias进行反序列化时,除了支持指定的别名,还支持使用原来的字段名(这一点在Jackson中比较少见)。

JsonAlias别名不支持序列化

JsonAlias别名只用于反序列化,并不支持序列化。

    /**
     * 序列化时,使用原来的字段名,不会替换为别名
     * 
     * @throws JsonProcessingException
     */
    @Test
    public void serializeNoEffect() throws JsonProcessingException {
        Article article = new Article();
        article.setTitle("spring");
        article.setContent("flowers");
    
        ObjectMapper mapper = new ObjectMapper();
        System.out.println(mapper.writeValueAsString(article));
    }

执行结果:

    {"title":"spring","content":"flowers"}

JsonGetter和JsonSetter

使用JsonGetter和JsonSetter注解,可以实现类似于JsonProperty和JsonAlias修改字段名的功能。同时,可以实现setter和getter使用不同的名称。

序列化和反序列化使用相同的别名

    public class Article1 {
    
        private String title;
        private String content;
    
        @JsonGetter("t")
        public String getTitle() {
            return title;
        }
    
        @JsonSetter("t")
        public void setTitle(String title) {
            this.title = title;
        }
    
        @JsonGetter("c")
        public String getContent() {
            return content;
        }
    
        @JsonSetter("c")
        public void setContent(String content) {
            this.content = content;
        }
    
    }

1)使用注解的别名进行序列化

    /**
     * 使用注解的别名进行序列化
     * 
     * @throws JsonProcessingException
     */
    @Test
    public void serialize1() throws JsonProcessingException {
        Article1 article = new Article1();
        article.setTitle("spring");
        article.setContent("flowers");
    
        ObjectMapper mapper = new ObjectMapper();
        System.out.println(mapper.writeValueAsString(article));
    }

执行结果:

    {"t":"spring","c":"flowers"}

2)反序列化时,只支持使用字段别名,不支持使用原字段名

    /**
     * 反序列化时,只支持使用字段别名
     * 
     * @throws JsonMappingException
     * @throws JsonProcessingException
     */
    @Test
    public void deserialize1() throws JsonMappingException, JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
    
        // 不支持使用原字段名
    //  String jsonString = "{\"title\":\"spring\",\"content\":\"flowers\"}";
        // 正确. 使用别名
        String jsonString = "{\"t\":\"spring\",\"c\":\"flowers\"}";
        Article1 article = mapper.readValue(jsonString, Article1.class);
        System.out.println(mapper.writeValueAsString(article));
    }

执行结果:

    {"t":"spring","c":"flowers"}

3)同一字段只使用JsonGetter和JsonSetter任一个注解 如果只给字段添加JsonGetter注解,或者只添加JsonSetter注解。在序列化和反序列化时,都需要统一用注解的名称。

    public class Article2 {
    
        private String title;
        private String content;
    
        @JsonGetter("t")
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public String getContent() {
            return content;
        }
    
        @JsonSetter("c")
        public void setContent(String content) {
            this.content = content;
        }
    
    }

getContent()方法没注解,序列化时别名和JsonSetter注解相同,名称为c。

    /**
     * 使用注解的别名进行序列化
     * 
     * @throws JsonProcessingException
     */
    @Test
    public void serialize2() throws JsonProcessingException {
        Article2 article = new Article2();
        article.setTitle("spring");
        article.setContent("flowers");
    
        ObjectMapper mapper = new ObjectMapper();
        System.out.println(mapper.writeValueAsString(article));
    }

执行结果:

    {"t":"spring","c":"flowers"}

setTitle()方法没注解,反序列化时别名和JsonGetter注解相同,名称为t。

    /**
     * 反序列化时,只支持使用字段别名
     * 
     * @throws JsonMappingException
     * @throws JsonProcessingException
     */
    @Test
    public void deserialize2() throws JsonMappingException, JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
    
        // 不支持使用原字段名
    //  String jsonString = "{\"title\":\"spring\",\"content\":\"flowers\"}";
        // 不支持. 即使setTitle()方法没有注解,也需要使用JsonGetter注解的别名.
    //  String jsonString = "{\"title\":\"spring\",\"c\":\"flowers\"}";
        // 正确. 都使用别名
        String jsonString = "{\"t\":\"spring\",\"c\":\"flowers\"}";
        Article2 article = mapper.readValue(jsonString, Article2.class);
        System.out.println(mapper.writeValueAsString(article));
    }

执行结果:

    {"t":"spring","c":"flowers"}

虽然只给getTitle()方法添加了JsonGetter注解,但即使setTitle()方法没有注解,也需要使用JsonGetter注解的别名进行反序列化。

序列化和反序列化使用不同的名称

    public class Article3 {
    
        private String title;
        private String content;
    
        @JsonGetter("tr")
        public String getTitle() {
            return title;
        }
    
        @JsonSetter("tw")
        public void setTitle(String title) {
            this.title = title;
        }
    
        @JsonGetter("cr")
        public String getContent() {
            return content;
        }
    
        @JsonSetter("cw")
        public void setContent(String content) {
            this.content = content;
        }
    
    }

1)使用JsonGetter注解的别名进行序列化

    /**
     * 使用JsonGetter注解的别名进行序列化
     * 
     * @throws JsonProcessingException
     */
    @Test
    public void serialize3() throws JsonProcessingException {
        Article3 article = new Article3();
        article.setTitle("spring");
        article.setContent("flowers");
    
        ObjectMapper mapper = new ObjectMapper();
        System.out.println(mapper.writeValueAsString(article));
    }

执行结果:

    {"tr":"spring","cr":"flowers"}

2)使用JsonSetter注解的别名进行反序列化

    /**
     * 使用JsonSetter注解的别名进行反序列化
     * 
     * @throws JsonMappingException
     * @throws JsonProcessingException
     */
    @Test
    public void deserialize3() throws JsonMappingException, JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
    
        String jsonString = "{\"tw\":\"spring\",\"cw\":\"flowers\"}";
        Article3 article = mapper.readValue(jsonString, Article3.class);
        System.out.println(mapper.writeValueAsString(article));
    }

执行结果:

    {"tr":"spring","cr":"flowers"}

小结

Jackson为字段命名提供了多种实现方式,用来灵活的支持各种应用场景。 使用JsonProperty注解是比较常用的统一字段命名方式,使用简单。 使用命名策略可以实现比较复杂的命名规则,可以通过扩展PropertyNamingStrategy来自定义命名规则。 JsonAlias支持指定多个别名,但只能应用于反序列化操作。 在序列化和反序列化时,使用JsonGetter和JsonSetter组合,既可以给字段统一命名,也可以分别使用不同的名称。 有了以上几种命名方式,给字段命名提供了非常丰富的选择空间。

参考

https://github.com/FasterXML/jackson (opens in a new tab)

https://www.logicbig.com/tutorials/misc/jackson/renaming-properties.html (opens in a new tab)