领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多2015/08/31 更新,增加了 Jackson 模块部分
Spring 对 Jackson 的支持最近得到了改进,使其更加灵活和强大。这篇博文将为您提供有关 Spring Framework 4.x 和 Spring Boot 中可用的最有用的 Jackson 相关功能的更新。所有代码示例均来自此 spring-jackson-demo 示例应用程序,欢迎查看代码。
有时需要对序列化到 HTTP 响应主体中的对象进行上下文过滤。为了提供此类功能,Spring MVC 现在内置支持 Jackson 的序列化视图(从 Spring Framework 4.2 开始,JSON 视图也支持 @MessageMapping
处理程序方法)。
以下示例说明了如何使用 @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);
}
}
在此示例中,如果检索所有消息,则由于使用 @JsonView(View.Summary.class)
注释的 getAllMessages()
方法,只会序列化最重要的字段。
[ {
"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()
处理程序方法(未指定 JSON 视图)检索特定 Message
时,将按预期序列化所有字段。
{
"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 视图层次结构(如果某个字段是 JSON 视图的一部分,它也将是父视图的一部分)。例如,此处理程序方法将序列化使用 @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
时,也可以指定 JSON 视图,方法是将要序列化的值包装在 MappingJacksonValue
中,如本 代码示例 所示。
如 参考文档 中所述,可以通过声明扩展 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",
...
});
JSONP 也受支持,并在使用 MappingJackson2JsonView
和具有名为 jsonp 或 callback 的查询参数的请求时自动启用。可以通过 jsonpParameterNames
属性自定义 JSONP 查询参数名称。
从 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
使用 ObjectMapper
的默认配置。为了提供更好且易于自定义的默认配置,引入了新的 Jackson2ObjectMapperBuilder
。它是众所周知的 Jackson2ObjectMapperFactoryBean
(用于 XML 配置)的 JavaConfig 等效项。
Jackson2ObjectMapperBuilder
提供了一个不错的 API 来自定义各种 Jackson 设置,同时保留 Spring Framework 提供的默认设置。它还允许基于相同的配置创建 ObjectMapper
和 XmlMapper
实例。
Jackson2ObjectMapperBuilder
和 Jackson2ObjectMapperFactoryBean
都定义了更好的 Jackson 默认配置。例如,将 DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
属性设置为 false,以允许反序列化具有未映射属性的 JSON 对象。
这些类还允许您轻松注册 Jackson mixin、模块、序列化器,甚至属性命名策略(如 PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES
),如果您希望将 userName
java 属性转换为 JSON 中的 user_name
。
如 Spring Boot 参考文档中所述,有多种方法可以 自定义 Jackson ObjectMapper
。
例如,您可以通过将 spring.jackson.serialization.indent_output=true
等属性添加到 application.properties
中来轻松启用/禁用 Jackson 功能。
或者,Spring Boot 还允许通过声明 Jackson2ObjectMapperBuilder
@Bean
来自定义 Spring MVC HttpMessageConverter
使用的 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
,如下所示
@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()));
}
}
如果在类路径上检测到一些知名的 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 上下文感知的 HandlerInstantiator
(有关更多详细信息,请参阅 SPR-10768),您能够自动装配 Jackson 处理程序(序列化器、反序列化器、类型和类型 ID 解析器)。
这可以让您构建一个自定义的反序列化器,例如,它将替换 JSON 负载中仅包含引用的字段,并将其替换为从数据库检索到的完整 Entity
。