領先一步
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 開始,@MessageMapping 處理方法也支援 JSON 檢視)。
以下示例說明了如何使用 @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() 處理方法檢索特定 Message 時(未指定 JSON 檢視),所有欄位都按預期序列化。
{
"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 時,也可以透過將要序列化的值封裝在 MappingJacksonValue 中來指定 JSON 檢視,如本程式碼示例所示。
如參考文件所述,您可以透過宣告一個擴充套件 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 使用 ObjectMapper 的預設配置。為了提供更好且易於自定義的預設配置,引入了新的 Jackson2ObjectMapperBuilder。它是 XML 配置中眾所周知的 Jackson2ObjectMapperFactoryBean 的 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。
例如,您可以透過向 application.properties 新增 spring.jackson.serialization.indent_output=true 等屬性,輕鬆啟用/停用 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。