保持领先
VMware 提供培训和认证,助您加速进步。
了解更多于 2015/08/31 更新,新增 Jackson 模块部分
Spring 对 Jackson 的支持最近得到了改进,变得更加灵活和强大。这篇博文向您介绍 Spring Framework 4.x 和 Spring Boot 中最实用的 Jackson 相关特性。所有代码示例均来自这个 spring-jackson-demo 示例应用,欢迎查看代码。
有时,对序列化到 HTTP 响应体的对象进行上下文过滤非常有用。为了提供这样的能力,Spring MVC 现在内置支持 Jackson 的序列化视图 (Serialization Views)(自 Spring Framework 4.2 起,@MessageMapping
处理器方法也支持 JSON Views)。
以下示例演示了如何使用 @JsonView
根据序列化上下文过滤字段——例如,处理集合时获取“摘要”视图,处理单个资源时获取完整表示。
public class View {
interface Summary {}
}
public class User {
@JsonView(View.Summary.class)
private Long id;
@JsonView(View.Summary.class)
private String firstname;
@JsonView(View.Summary.class)
private String lastname;
private String email;
private String address;
private String postalCode;
private String city;
private String country;
}
public class Message {
@JsonView(View.Summary.class)
private Long id;
@JsonView(View.Summary.class)
private LocalDate created;
@JsonView(View.Summary.class)
private String title;
@JsonView(View.Summary.class)
private User author;
private List<User> recipients;
private String body;
}
借助 Spring MVC @JsonView
支持,可以按每个处理器方法选择哪些字段应该被序列化。
@RestController
public class MessageController {
@Autowired
private MessageService messageService;
@JsonView(View.Summary.class)
@RequestMapping("/")
public List<Message> getAllMessages() {
return messageService.getAll();
}
@RequestMapping("/{id}")
public Message getMessage(@PathVariable Long id) {
return messageService.get(id);
}
}
在此示例中,如果检索所有消息,由于 getAllMessages()
方法使用 @JsonView(View.Summary.class)
进行注解,因此只会序列化最重要的字段。
[ {
"id" : 1,
"created" : "2014-11-14",
"title" : "Info",
"author" : {
"id" : 1,
"firstname" : "Brian",
"lastname" : "Clozel"
}
}, {
"id" : 2,
"created" : "2014-11-14",
"title" : "Warning",
"author" : {
"id" : 2,
"firstname" : "Stéphane",
"lastname" : "Nicoll"
}
}, {
"id" : 3,
"created" : "2014-11-14",
"title" : "Alert",
"author" : {
"id" : 3,
"firstname" : "Rossen",
"lastname" : "Stoyanchev"
}
} ]
在 Spring MVC 的默认配置中,MapperFeature.DEFAULT_VIEW_INCLUSION
设置为 false
。这意味着启用 JSON 视图时,未注解的字段或属性(例如 body
或 recipients
)不会被序列化。
当使用 getMessage()
处理器方法检索特定 Message
(未指定 JSON View)时,所有字段都会按预期序列化。
{
"id" : 1,
"created" : "2014-11-14",
"title" : "Info",
"body" : "This is an information message",
"author" : {
"id" : 1,
"firstname" : "Brian",
"lastname" : "Clozel",
"email" : "[email protected]",
"address" : "1 Jaures street",
"postalCode" : "69003",
"city" : "Lyon",
"country" : "France"
},
"recipients" : [ {
"id" : 2,
"firstname" : "Stéphane",
"lastname" : "Nicoll",
"email" : "[email protected]",
"address" : "42 Obama street",
"postalCode" : "1000",
"city" : "Brussel",
"country" : "Belgium"
}, {
"id" : 3,
"firstname" : "Rossen",
"lastname" : "Stoyanchev",
"email" : "[email protected]",
"address" : "3 Warren street",
"postalCode" : "10011",
"city" : "New York",
"country" : "USA"
} ]
}
@JsonView
注解只能指定一个类或接口,但您可以使用继承来表示 JSON View 的层级结构(如果一个字段是某个 JSON View 的一部分,它也将是其父视图的一部分)。例如,此处理器方法将序列化使用 @JsonView(View.Summary.class)
和 @JsonView(View.SummaryWithRecipients.class)
进行注解的字段。
public class View {
interface Summary {}
interface SummaryWithRecipients extends Summary {}
}
public class Message {
@JsonView(View.Summary.class)
private Long id;
@JsonView(View.Summary.class)
private LocalDate created;
@JsonView(View.Summary.class)
private String title;
@JsonView(View.Summary.class)
private User author;
@JsonView(View.SummaryWithRecipients.class)
private List<User> recipients;
private String body;
}
@RestController
public class MessageController {
@Autowired
private MessageService messageService;
@JsonView(View.SummaryWithRecipients.class)
@RequestMapping("/with-recipients")
public List<Message> getAllMessagesWithRecipients() {
return messageService.getAll();
}
}
使用 RestTemplate
HTTP 客户端或 MappingJackson2JsonView
时,也可以通过将要序列化的值包装在 MappingJacksonValue
中来指定 JSON Views,如本代码示例所示。
如参考文档所述,您可以通过声明一个扩展 AbstractJsonpResponseBodyAdvice
的 @ControllerAdvice
bean 来为 @ResponseBody
和 ResponseEntity
方法启用 JSONP,如下所示。
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}
注册此类 @ControllerAdvice
bean 后,就可以使用 <script />
标签从另一个域请求 JSON Web 服务。
<script type="application/javascript"
src="http://mydomain.com/1.json?jsonp=parseResponse">
</script>
在此示例中,接收到的负载将是
parseResponse({
"id" : 1,
"created" : "2014-11-14",
...
});
使用 MappingJackson2JsonView
并且请求包含名为 jsonp 或 callback 的查询参数时,JSONP 也受支持并自动启用。JSONP 查询参数名称可以通过 jsonpParameterNames
属性进行自定义。
自 2.0 版本发布以来,Jackson 为除 JSON 外的其他数据格式提供了第一类支持。Spring Framework 和 Spring Boot 内置支持基于 Jackson 的 XML 序列化/反序列化。
一旦您在项目中包含 jackson-dataformat-xml
依赖,它将自动取代 JAXB2 使用。
使用 Jackson XML 扩展相对于 JAXB2 有几个优势
@XmlRootElement
注解类,每个可在 JSON 中序列化的类都可在 XML 中序列化您通常还希望确保正在使用的 XML 库是 Woodstox,因为
要使用它,只需将最新的 woodstox-core-asl
依赖添加到您的项目中。
在 Spring Framework 4.1.1 之前,Jackson HttpMessageConverter
s 使用 ObjectMapper
的默认配置。为了提供更好且易于自定义的默认配置,引入了新的 Jackson2ObjectMapperBuilder
。它是 XML 配置中众所周知的 Jackson2ObjectMapperFactoryBean
的 JavaConfig 等效项。
Jackson2ObjectMapperBuilder
提供了一个优秀的 API,用于自定义各种 Jackson 设置,同时保留 Spring Framework 提供的默认设置。它还允许基于相同的配置创建 ObjectMapper
和 XmlMapper
实例。
Jackson2ObjectMapperBuilder
和 Jackson2ObjectMapperFactoryBean
都定义了更好的 Jackson 默认配置。例如,将 DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
属性设置为 false,以便允许反序列化包含未映射属性的 JSON 对象。
这些类还允许您轻松注册 Jackson mixins、模块、序列化器,甚至属性命名策略,例如如果您希望将 Java 属性 userName
在 JSON 中转换为 user_name
,可以使用 PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES
。
如 Spring Boot 参考文档所述,有多种方法可以自定义 Jackson ObjectMapper
。
例如,您可以通过将 spring.jackson.serialization.indent_output=true
等属性添加到 application.properties
来轻松启用/禁用 Jackson 特性。
作为替代方案,Spring Boot 还允许通过声明 Jackson2ObjectMapperBuilder
@Bean
来自定义 Spring MVC HttpMessageConverter
s 使用的 Jackson 配置(JSON 和 XML)。
@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder b = new Jackson2ObjectMapperBuilder();
b.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
return b;
}
如果您想使用未通过常规配置键公开的高级 Jackson 配置,这将很有用。
如果您只需要注册一个额外的 Jackson 模块,请注意 Spring Boot 会自动检测所有 Module
@Bean
。例如,要注册 jackson-module-parameter-names
@Bean
public Module parameterNamesModule() {
return new ParameterNamesModule(JsonCreator.Mode.PROPERTIES);
}
在纯粹的 Spring Framework 应用中,您也可以使用 Jackson2ObjectMapperBuilder
来自定义 XML 和 JSON HttpMessageConverter
s,如下所示。
@Configuration
@EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
}
}
如果在 classpath 中检测到某些知名的 Jackson 模块,它们会自动注册
java.nio.file.Path
(自 4.2.1 版本起)Optional
(自 4.2.0 版本起)默认情况下不会注册一些其他模块(主要是因为它们需要额外的配置),因此您需要显式注册它们,例如使用 Jackson2ObjectMapperBuilder#modulesToInstall()
,或者如果您使用 Spring Boot,可以声明一个 Jackson Module
@Bean
javax.money
类型(非官方模块)自 Spring Framework 4.1.3 起,由于添加了 Spring context感知的 HandlerInstantiator
(详情请参阅 SPR-10768),您能够自动注入 Jackson 处理器(序列化器、反序列化器、类型和类型 ID 解析器)。
例如,这可以允许您构建一个自定义反序列化器,用从数据库检索的完整 Entity
替换 JSON 负载中仅包含引用的字段。