保持領先
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 負載中僅包含引用的欄位。