概览

上一篇文章介绍了Jackson中的映射器ObjectMapper,以及如何使用它来实现Json与Java对象之间的序列化和反序列化,最后介绍了Jackson中一些序列化/反序列化的高级特性。而本文将会介绍Jackson中的一些常用的(序列化/反序列化)注解,并且通过示例来演示如何使用这些注解,从而来提高我们在处理Json上的工作效率。


序列化注解

@JsonAnyGetter

@JsonAnyGetter注解允许灵活地使用映射(键值对,如Map)字段作为标准属性。

我们声明如下Java类:

@Data
@Accessors(chain = true)
public static class ExtendableBean {public String name;private Map<String, String> properties;@JsonAnyGetterpublic Map<String, String> getProperties() {return properties;}
}

编写测试代码,测试@JsonAnyGetter:

@Test
public void testJsonAnyGetter() throws JsonProcessingException {ExtendableBean extendableBean = new ExtendableBean();Map<String, String> map = new HashMap<>();map.put("age", "13");extendableBean.setName("dxsn").setProperties(map);log.info(new ObjectMapper().writeValueAsString(extendableBean));//打印:{"name":"dxsn","age":"13"}assertThat(new ObjectMapper().writeValueAsString(extendableBean)).contains("name");assertThat(new ObjectMapper().writeValueAsString(extendableBean)).contains("age");
}

如上,可以看properties属性中的键值对(Map)被扩展到了ExtendableBean的Json对象中。

@JsonGetter

@JsonGetter注解是@JsonProperty注解的替代品,用来将一个方法标记为getter方法。

我们创建以下Java类

@Data
@AllArgsConstructor
@NoArgsConstructor
public static class MyBean {public int id;private String name;@JsonGetter("name")public String getTheName() {return name;}
}

如上,我们在类中声明了一个getTheName()方法,并且使用@JsonGetter(“name”)修饰,此时,该方法将会被Jackson认作是name属性的get方法。

编写测试代码:

@Test
public void testJsonGetter() throws JsonProcessingException {MyBean myBean = new MyBean(1, "dxsn");String jsonStr = new ObjectMapper().writeValueAsString(myBean);log.info(jsonStr);assertThat(jsonStr).contains("id");assertThat(jsonStr).contains("name");
}

可以看到,jackson将私有属性name,也进行了序列化。

@JsonPropertyOrder

我们可以使用@JsonPropertyOrder注解来指定Java对象的属性序列化顺序。

@JsonPropertyOrder({"name", "id"})
//order by key's name
//@JsonPropertyOrder(alphabetic = true)
@Data
@Accessors(chain = true)
public static class MyOrderBean {public int id;public String name;
}

编写测试代码:

@Test
public void testJsonPropertyOrder1() throws JsonProcessingException {MyOrderBean myOrderBean = new MyOrderBean().setId(1).setName("dxsn");String jsonStr = new ObjectMapper().writeValueAsString(myOrderBean);log.info(jsonStr);assertThat(jsonStr).isEqualTo("{\"name\":\"dxsn\",\"id\":1}");
}

如上,可以看到序列化得到的Json对象中属性的排列顺序正是我们在注解中指定的顺序。

@JsonRawValue

@JsonRawValue注解可以指示Jackson按原样序列化属性。

在下面的例子中,我们使用@JsonRawValue嵌入一些定制的JSON作为一个实体的值:

@Data
@AllArgsConstructor
@NoArgsConstructor
public static class RawBean {public String name;@JsonRawValuepublic String json;
}

编写测试代码:

@Test
public void testJsonRawValue() throws JsonProcessingException {RawBean rawBean = new RawBean("dxsn", "{\"love\":\"true\"}");log.info(new ObjectMapper().writeValueAsString(rawBean));//输出:{"name":"dxsn","json":{"love":"true"}}String result = new ObjectMapper().writeValueAsString(rawBean);assertThat(result).contains("dxsn");assertThat(result).contains("{\"love\":\"true\"}");
}

@JsonValue

@JsonValue表示Jackson将使用一个方法来序列化整个实例。

下面我们创建一个枚举类:

@AllArgsConstructor
public static enum TypeEnumWithValue {TYPE1(1, "Type A"), TYPE2(2, "Type 2");private Integer id;private String name;@JsonValuepublic String getName() {return name;}
}

如上,我们在getName()上使用@JsonValue进行修饰。

编写测试代码:

@Test
public void testJsonValue() throws JsonProcessingException {String  jsonStr = new ObjectMapper().writeValueAsString(TypeEnumWithValue.TYPE2);log.info(jsonStr);assertThat(jsonStr).isEqualTo("Type 2");
}

可以看到,枚举类的对象序列化后的值即getName()方法的返回值。

@JsonRootName

如果启用了包装(wrapping),则使用@JsonRootName注解可以指定要使用的根包装器的名称。

下面我们创建一个使用@JsonRootName修饰的Java类:

@JsonRootName(value = "user")
@Data
@AllArgsConstructor
public static class UserWithRoot {public int id;public String name;
}

编写测试:

@Test
public void testJsonRootName() throws JsonProcessingException {UserWithRoot userWithRoot = new UserWithRoot(1, "dxsn");ObjectMapper mapper = new ObjectMapper();//⬇️重点!!!mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);String result = mapper.writeValueAsString(userWithRoot);log.info(result);//输出:{"user":{"id":1,"name":"dxsn"}}assertThat(result).contains("dxsn");assertThat(result).contains("user");
}

上面代码中,我们通过开启ObjectMapper的SerializationFeature.WRAP_ROOT_VALUE。可以看到UserWithRoot对象被序列化后的Json对象被包装在user中,而非单纯的{"id":1,"name":"dxsn"}

@JsonSerialize

@JsonSerialize注解表示序列化实体时要使用的自定义序列化器。

我们定义一个自定义的序列化器:

public static class CustomDateSerializer extends StdSerializer<Date> {private static SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");public CustomDateSerializer() {this(null);}public CustomDateSerializer(Class<Date> t) {super(t);}@Overridepublic void serialize(Date value, JsonGenerator gen, SerializerProvider arg2) throws IOException, JsonProcessingException {gen.writeString(formatter.format(value));}
}

使用自定义的序列化器,创建Java类:

@Data
@AllArgsConstructor
public static class Event {public String name;@JsonSerialize(using = CustomDateSerializer.class)public Date eventDate;
}

编写测试代码:

@Test
public void testJsonSerialize() throws ParseException, JsonProcessingException {SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");String toParse = "20-12-2014 02:30:00";Date date = formatter.parse(toParse);Event event = new Event("party", date);String result = new ObjectMapper().writeValueAsString(event);assertThat(result).contains(toParse);
}

可以看到,使用@JsonSerialize注解修饰指定属性后,将会使用指定的序列化器来序列化该属性。


反序列化注解

@JsonCreator

我们可以使用@JsonCreator注解来优化/替换反序列化中使用的构造器/工厂。

当我们需要反序列化一些与我们需要获取的目标实体不完全匹配的JSON时,它非常有用。

现在,有如下一个Json对象:

{"id":1,"theName":"My bean"}

我们声名了一个Java类:

@Data
public static class BeanWithCreator {private int id;private String name;
}

此时,在我们的目标实体中没有theName字段,只有name字段。现在,我们不想改变实体本身,此时可以通过使用@JsonCreator和@JsonProperty注解来修饰构造函数:

@Data
public static class BeanWithCreator {private int id;private String name;@JsonCreatorpublic BeanWithCreator(@JsonProperty("id") int id, @JsonProperty("theName") String name) {this.id = id;this.name = name;}
}

编写测试:

@Test
public void beanWithCreatorTest() throws JsonProcessingException {String str = "{\"id\":1,\"theName\":\"My bean\"}";BeanWithCreator bean = new ObjectMapper().readerFor(BeanWithCreator.class).readValue(str);assertThat(bean.getId()).isEqualTo(1);assertThat(bean.getName()).isEqualTo("My bean");
}

可以看到,即使Json对象中的字段名和实体类中不一样,但由于我们手动指定了映射字段的名字,从而反序列化成功。

@JacksonInject

@JacksonInject表示java对象中的属性将通过注入来赋值,而不是从JSON数据中获得其值。

创建如下实体类,其中有字段被@JacksonInject修饰:

public static class BeanWithInject {@JacksonInjectpublic int id;public String name;
}

编写测试:

@Test
public void jacksonInjectTest() throws JsonProcessingException {String json = "{\"name\":\"dxsn\"}";InjectableValues inject = new InjectableValues.Std().addValue(int.class, 1);BeanWithInject bean = new ObjectMapper().reader(inject).forType(BeanWithInject.class).readValue(json);assertThat(bean.id).isEqualTo(1);assertThat(bean.name).isEqualTo("dxsn");
}

如上,我们在测试中将json字符串(仅存在name字段)进行反序列化,其中id通过注入的方式对属性进行赋值。

@JsonAnySetter

@JsonAnySetter允许我们灵活地使用映射(键值对、Map)作为标准属性。在反序列化时,JSON的属性将被添加到映射中。

创建一个带有@JsonAnySetter的实体类:

public static class ExtendableBean {public String name;public Map<String, String> properties;@JsonAnySetterpublic void add(String key, String value) {if (properties == null) {properties = new HashMap<>();}properties.put(key, value);}
}

编写测试:

@Test
public void testJsonAnySetter() throws JsonProcessingException {String json = "{\"name\":\"dxsn\", \"attr2\":\"val2\", \"attr1\":\"val1\"}";ExtendableBean extendableBean = new ObjectMapper().readerFor(ExtendableBean.class).readValue(json);assertThat(extendableBean.name).isEqualTo("dxsn");assertThat(extendableBean.properties.size()).isEqualTo(2);
}

可以看到,json对象中的attr1,attr2属性在反序列化之后进入了properties。

@JsonSetter

@JsonSetter是@JsonProperty的替代方法,它将方法标记为属性的setter方法。
当我们需要读取一些JSON数据,但目标实体类与该数据不完全匹配时,这非常有用,因此我们需要优化使其适合该数据。

创建如下实体类:

@Data
public static class MyBean {public int id;private String name;@JsonSetter("name")public void setTheName(String name) {this.name = "hello " + name;}
}

编写测试:

@Test
public void testJsonSetter() throws JsonProcessingException {String json = "{\"id\":1,\"name\":\"dxsn\"}";MyBean bean = new ObjectMapper().readerFor(MyBean.class).readValue(json);assertThat(bean.getName()).isEqualTo("hello dxsn");
}

可以看到,json对象中的name属性为“dxsn”,我们通过在MyBean类中定义了使用@JsonSetter(“name”)注解修饰的方法,这表明该类的对象在反序列话的时候,name属性将来自此方法。最后MyBean对象中name的值变为了hello dxsn。

@JsonDeserialize

@JsonDeserialize注解指定了在反序列化的时候使用的反序列化器。

如下,定义了一个自定义的反序列化器:

public static class CustomDateDeserializer extends StdDeserializer<Date> {private static SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");public CustomDateDeserializer() {this(null);}public CustomDateDeserializer(Class<?> vc) {super(vc);}@Overridepublic Date deserialize(JsonParser jsonparser, DeserializationContext context) throws IOException {String date = jsonparser.getText();try {return formatter.parse(date);} catch (ParseException e) {throw new RuntimeException(e);}}
}

创建一个使用@JsonDeserialize(using = CustomDateDeserializer.class)修饰的实体类:

public static class Event {public String name;@JsonDeserialize(using = CustomDateDeserializer.class)public Date eventDate;
}

编写测试:

@Test
public void whenDeserializingUsingJsonDeserialize_thenCorrect()throws IOException {String json = "{\"name\":\"party\",\"eventDate\":\"20-12-2014 02:30:00\"}";SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");Event event = new ObjectMapper().readerFor(Event.class).readValue(json);assertThat(event.name).isEqualTo("party");assertThat(event.eventDate).isEqualTo(df.format(event.eventDate));}

可以看到,在Event对象中,eventDate属性通过自定义的反序列化器,将“20-12-2014 02:30:00”反序列化成了Date对象。

@JsonAlias

@JsonAlias在反序列化期间为属性定义一个或多个替代名称。让我们通过一个简单的例子来看看这个注解是如何工作的:

@Data
public static class AliasBean {@JsonAlias({"fName", "f_name"})private String firstName;private String lastName;
}

如上,我们编写了一个使用@JsonAlias修饰的AliasBean实体类。

编写测试:

@Test
public void whenDeserializingUsingJsonAlias_thenCorrect() throws IOException {String json = "{\"fName\": \"John\", \"lastName\": \"Green\"}";AliasBean aliasBean = new ObjectMapper().readerFor(AliasBean.class).readValue(json);assertThat(aliasBean.getFirstName()).isEqualTo("John");
}

可以看到,即使json对象中的字段名是fName,但是由于在AliasBean中使用@JsonAlias修饰了firstName属性,并且指定了两个别名。所以反序列化之后fName被映射到AliasBean对象的firstName属性上。


更多

除上述注解之外,Jackson还提供了很多额外的注解,这里不一一列举,接下来会例举几个常用的注解:

  • @JsonProperty:可以在类的指定属性上添加@JsonProperty注解来表示其对应在JSON中的属性名。
  • @JsonFormat:此注解在序列化对象中的日期/时间类型属性时可以指定一种字符串格式输出,如:@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = “dd-MM-yyyy hh:mm:ss”)。
  • @JsonUnwrapped:@JsonUnwrapped定义了在序列化/反序列化时应该被扁平化的值。
  • @JsonIgnore:序列化/反序列化时忽略被修饰的属性。

总结

本文主要介绍了Jackson常用的序列化/反序列化注解,最后介绍了几个常用的通用注解。Jackson中提供的注解除了本文列举的还有很多很多,使用注解可以让我们的序列化/反序列化工作更加轻松。如果你想将某库换成Jackson,希望这篇文章可以帮到你。

本文涉及的代码地址:https://gitee.com/jeker8chen/jackson-annotation-demo


除了FastJson,你也应该了解一下Jackson(二)相关推荐

  1. javabean与json转换(fastjson与jackson两个版本)

    [README] 本文演示了 javabean与json转换的开发方式: 要想 javabean的属性名 与 json的字段名不一致,也是可以转换的: 之前需要引入 ali.fastjson < ...

  2. 一文吃透Json解析工具类合集(FastJson、Jackson、Gson)

    Json解析工具类 前言 一.各个JSON技术的简介 1.1 FastJson角色 1.2 Jackson角色 1.3 Gson角色 二.使用步骤 2.1 引入库 1.FastJson依赖 2.Jac ...

  3. 深度对比Jackson和Fastjson,最终我还是选择了...

    点击关注公众号,Java干货及时送达 来源:cnblogs.com/larva-zhh/p/11544317.html 为什么要替换fastjson 框架选型 替换fastjson Deseriali ...

  4. fastjson 添加key value_FastJson迁移至Jackson

    FastJson迁移至Jackson 背景 Fastjson在短期内连续爆出高危漏洞, 导致每次都得改代码上线, 已经粉转黑, 决定迁移到SpringBoot自带的Jackson; 相关粉转黑文章参考 ...

  5. fastjson list转json字符串_从fastjson转jackson的血泪史

    最近线上项目频繁报警,运维大神在报警时导出了 java线程堆栈(jstack工具导出).统计分析后,总共运行中2003个线程,有1997个都是在做fastjson的反序列化操作.(简易分析工具可以使用 ...

  6. jackson、fastjson、kryo、protostuff等序列化工具性能对比

    简介 实际项目中,我们经常需要使用序列化工具来存储和传输对象.目前用得比较多的序列化工具有:jackson.fastjson.kryo.protostuff.fst 等,本文将简单对比这几款工具序列化 ...

  7. FastJson与Jackson对比

    以前一直用FastJson,最近一个项目里,由于引入的第三方包里导过fastjson,如果再次引用会导致重包,很尴尬,我就用Jackson. json字符串中,如果key的字母是大写,Jackson好 ...

  8. Gson、Fastjson和Jackson速度对比

    Gson 依赖 <dependency><groupId>com.google.code.gson</groupId><artifactId>gson& ...

  9. Gson与FastJson比较

    一. 简介和优劣 1.Google的Gson Gson是目前功能最全的Json解析神器,Gson当初是为因应Google公司内部需求而由Google自行研发而来,但自从在2008年五月公开发布第一版后 ...

最新文章

  1. CCNA和四级网工的对比
  2. 详解蚂蚁金服 SOFAJRaft:生产级高性能 Java 实现
  3. 【整理】MySQL 之 autocommit
  4. git 为什么不能断点_跟我一起学docker(九)--持续系统集成了解下git
  5. bert 多义词_BERT之后,GLUE基准升级为SuperGLUE:难度更大
  6. JavaNIO编程基础-图解JavaNIO内存模型
  7. 通过maven命令获取参数值
  8. word 页眉 页脚 页码 分页符 目录
  9. 铁威马教程之如何轻松同步TNAS和云盘数据
  10. AMiner发布2022 AI 2000人工智能最具影响力学者名单
  11. Infer静态分析-内存泄漏分析
  12. 用小鸟云服务器如何实现Nginx静态资源配置?
  13. python五角星程序显示错误_python画五角星和六角星程序
  14. C语言:甲乙丙三人放鞭炮,求鞭炮响声问题
  15. VI 与 VIM 编辑器实操演示(动图)
  16. 计算机培训ppt教案,课件制作的教案
  17. Python - faker
  18. 秒开率达90%:腾讯看点客户端 GIF 转视频优化方案
  19. Oracle - 基本入门
  20. UCHome二次开发–基本体系结构

热门文章

  1. 小程序全局注入-gio原理简单分析
  2. 短视频时代是靠什么赚钱的,介绍常见的5种方式,简单明了
  3. 如何令电子邀请函设计变得与众不同?
  4. 1.2.2 通用的标量输运方程|1.2.3 控制方程的分类(OpenFOAM理论笔记系列)
  5. 2019中国最佳创新公司50榜单发布
  6. 2021年电工(高级)考试题及电工(高级)考试报名
  7. 联想LECOO电脑显示器闪屏解决办法
  8. weblogic控制台配置修改
  9. 【ARM】ARM(2440)嵌入式开发全套视频教程|tiny210文档的阅读
  10. 高通和麒麟芯片isp处理性能对比,关乎视频拍摄处理速度