領先一步
VMware 提供訓練和認證,加速您的進度。
了解更多於 2015/08/31 更新,新增 Jackson 模組區段
Spring 的 Jackson 支援最近得到了改進,變得更加靈活和強大。這篇部落格文章將向您介紹 Spring Framework 4.x 和 Spring Boot 中可用的最有用的 Jackson 相關功能。所有程式碼範例都來自這個 spring-jackson-demo 範例應用程式,歡迎隨時查看程式碼。
有時過濾序列化到 HTTP 回應主體的物件會很有用。為了提供這種功能,Spring MVC 現在內建支援 Jackson 的序列化 Views(從 Spring Framework 4.2 開始,JSON Views 也支援在 @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 View 時,不會序列化未註釋的欄位或屬性,例如 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
時,也可以指定 JSON Views,方法是將要序列化的值包裝在 MappingJacksonValue
中,如這個 程式碼範例所示。
如 參考文件中所述,您可以透過宣告一個擴展 AbstractJsonpResponseBodyAdvice
的 @ControllerAdvice
bean,為 @ResponseBody
和 ResponseEntity
方法啟用 JSONP,如下所示。
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}
註冊了這樣一個 @ControllerAdvice
bean 後,就可以使用 <script />
標籤從另一個網域請求 JSON webservice。
<script type="application/javascript"
src="http://mydomain.com/1.json?jsonp=parseResponse">
</script>
在本範例中,收到的 payload 將是
parseResponse({
"id" : 1,
"created" : "2014-11-14",
...
});
使用帶有查詢參數 jsonp 或 callback 的請求,在使用 MappingJackson2JsonView
時也支援並自動啟用 JSONP。JSONP 查詢參數名稱可以透過 jsonpParameterNames
屬性進行自訂。
自 2.0 版本以來,Jackson 為 JSON 以外的一些其他資料格式提供了一流的支援。Spring Framework 和 Spring Boot 提供內建支援,可基於 Jackson 進行 XML 序列化/反序列化。
只要將 jackson-dataformat-xml
依賴項包含到您的專案中,它就會自動取代 JAXB2 使用。
與 JAXB2 相比,使用 Jackson XML 擴充具有多個優點:
@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 mixins、模組、序列化器,甚至屬性命名策略(如 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 模組在類別路徑 (classpath) 上被偵測到,它們會自動註冊。
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),您可以自動注入 (autowire) Jackson 處理器 (序列化器、反序列化器、類型和類型 ID 解析器)。
例如,這允許您建立一個自訂的反序列化器,它會將 JSON Payload 中只包含參考的欄位,替換為從資料庫檢索的完整 Entity
。