Spring Boot 之使用 Json 詳解
簡介
Spring Boot 支持的 Json 庫
Spring Boot 支持三種 Json 庫:
- Gson
- Jackson
- JSON-B
Jackson 是 Spring Boot 官方推薦的默認庫。
Spring Boot 提供了 Jackson 的自動配置,Jackson 是 spring-boot-starter-json
的一部分。當 Jackson 在類路徑上時,會自動配置 ObjectMapper bean。
Spring Boot 提供了 Gson 的自動配置。當 Gson 在 classpath 上時,會自動配置 Gson bean。提供了幾個 spring.gson.*
配置屬性來自定義配置。為了獲得更多控制,可以使用一個或多個 GsonBuilderCustomizer
bean。
Spring Boot 提供了 JSON-B 的自動配置。當 JSON-B API 在 classpath 上時,將自動配置 Jsonb bean。首選的 JSON-B 實現是 Apache Johnzon,它提供了依賴關系管理。
Spring Web 中的序列化、反序列化
以下注解都是 spring-web
中提供的支持。
@ResponseBody
@Responsebody
注解用於將 Controller 的方法返回的對象,通過適當的 HttpMessageConverter
轉換為指定格式后,寫入到 HTTP Response 對象的 body 數據區。一般在異步獲取數據時使用。通常是在使用 @RequestMapping
后,返回值通常解析為跳轉路徑,加上 @Responsebody 后返回結果不會被解析為跳轉路徑,而是直接寫入 HTTP 響應正文中。
示例:
@ResponseBody
@RequestMapping(name = "/getInfo", method = RequestMethod.GET)
public InfoDTO getInfo() {
return new InfoDTO();
}
@RequestBody
@RequestBody 注解用於讀取 HTTP Request 請求的 body 部分數據,使用系統默認配置的 HttpMessageConverter
進行解析,然后把相應的數據綁定到要返回的對象上;再把 HttpMessageConverter
返回的對象數據綁定到 controller 中方法的參數上。
request 的 body 部分的數據編碼格式由 header 部分的 Content-Type
指定。
示例:
@RequestMapping(name = "/postInfo", method = RequestMethod.POST)
public void postInfo(@RequestBody InfoDTO infoDTO) {
// ...
}
@RestController
Spring 4 以前:
如果需要返回到指定頁面,則需要用 @Controller
配合視圖解析器 InternalResourceViewResolver
。
如果需要返回 JSON,XML 或自定義 mediaType 內容到頁面,則需要在對應的方法上加上 @ResponseBody
注解。
Spring 4 以后,新增了 @RestController
注解:
它相當於 @Controller
+ @RequestBody
。
如果使用 @RestController
注解 Controller,則 Controller 中的方法無法返回 jsp 頁面,或者 html,配置的視圖解析器 InternalResourceViewResolver
將不起作用,直接返回內容。
指定類的 Json 序列化、反序列化
如果使用 Jackson 序列化和反序列化 JSON 數據,您可能需要編寫自己的 JsonSerializer
和 JsonDeserializer
類。自定義序列化程序通常通過模塊向 Jackson 注冊,但 Spring Boot 提供了另一種 @JsonComponent
注釋,可以更容易地直接注冊 Spring Beans。
您可以直接在 JsonSerializer
或 JsonDeserializer
實現上使用 @JsonComponent
注釋。您還可以在包含序列化程序/反序列化程序作為內部類的類上使用它,如以下示例所示:
import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import org.springframework.boot.jackson.*;
@JsonComponent
public class Example {
public static class Serializer extends JsonSerializer<SomeObject> {
// ...
}
public static class Deserializer extends JsonDeserializer<SomeObject> {
// ...
}
}
ApplicationContext
中的所有 @JsonComponent
bean 都會自動注冊到 Jackson。因為 @JsonComponent
是使用 @Component
進行元注釋的,所以通常的組件掃描規則適用。
Spring Boot 還提供了 JsonObjectSerializer
和 JsonObjectDeserializer
基類,它們在序列化對象時提供了標准 Jackson 版本的有用替代方法。有關詳細信息,請參閱 Javadoc 中的 JsonObjectSerializer
和 JsonObjectDeserializer
。
@JsonTest
使用 @JsonTest
可以很方便的在 Spring Boot 中測試序列化、反序列化。
使用 @JsonTest
相當於使用以下自動配置:
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration org.springframework.boot.test.autoconfigure.json.JsonTestersAutoConfiguration
@JsonTest
使用示例:
想試試完整示例,可以參考:源碼
@JsonTest
@RunWith(SpringRunner.class)
public class SimpleJsonTest {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
private JacksonTester<InfoDTO> json;
@Test
public void testSerialize() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
InfoDTO infoDTO = new InfoDTO("JSON測試應用", "1.0.0", sdf.parse("2019-01-01 12:00:00"));
JsonContent<InfoDTO> jsonContent = json.write(infoDTO);
log.info("json content: {}", jsonContent.getJson());
// 或者使用基於JSON path的校驗
assertThat(jsonContent).hasJsonPathStringValue("@.appName");
assertThat(jsonContent).extractingJsonPathStringValue("@.appName").isEqualTo("JSON測試應用");
assertThat(jsonContent).hasJsonPathStringValue("@.version");
assertThat(jsonContent).extractingJsonPathStringValue("@.version").isEqualTo("1.0.0");
assertThat(jsonContent).hasJsonPathStringValue("@.date");
assertThat(jsonContent).extractingJsonPathStringValue("@.date").isEqualTo("2019-01-01 12:00:00");
}
@Test
public void testDeserialize() throws Exception {
String content = "{\"appName\":\"JSON測試應用\",\"version\":\"1.0.0\",\"date\":\"2019-01-01\"}";
InfoDTO actual = json.parseObject(content);
assertThat(actual.getAppName()).isEqualTo("JSON測試應用");
assertThat(actual.getVersion()).isEqualTo("1.0.0");
}
}
Spring Boot 中的 json 配置
Jackson 配置
當 Spring Boot 的 json 庫為 jackson 時,可以使用以下配置屬性(對應 JacksonProperties
類):
spring.jackson.date-format= # Date format string or a fully-qualified date format class name. For instance, `yyyy-MM-dd HH:mm:ss`.
spring.jackson.default-property-inclusion= # Controls the inclusion of properties during serialization. Configured with one of the values in Jackson's JsonInclude.Include enumeration.
spring.jackson.deserialization.*= # Jackson on/off features that affect the way Java objects are deserialized.
spring.jackson.generator.*= # Jackson on/off features for generators.
spring.jackson.joda-date-time-format= # Joda date time format string. If not configured, "date-format" is used as a fallback if it is configured with a format string.
spring.jackson.locale= # Locale used for formatting.
spring.jackson.mapper.*= # Jackson general purpose on/off features.
spring.jackson.parser.*= # Jackson on/off features for parsers.
spring.jackson.property-naming-strategy= # One of the constants on Jackson's PropertyNamingStrategy. Can also be a fully-qualified class name of a PropertyNamingStrategy subclass.
spring.jackson.serialization.*= # Jackson on/off features that affect the way Java objects are serialized.
spring.jackson.time-zone= # Time zone used when formatting dates. For instance, "America/Los_Angeles" or "GMT+10".
spring.jackson.visibility.*= # Jackson visibility thresholds that can be used to limit which methods (and fields) are auto-detected.
GSON 配置
當 Spring Boot 的 json 庫為 gson 時,可以使用以下配置屬性(對應 GsonProperties
類):
spring.gson.date-format= # Format to use when serializing Date objects.
spring.gson.disable-html-escaping= # Whether to disable the escaping of HTML characters such as '<', '>', etc.
spring.gson.disable-inner-class-serialization= # Whether to exclude inner classes during serialization.
spring.gson.enable-complex-map-key-serialization= # Whether to enable serialization of complex map keys (i.e. non-primitives).
spring.gson.exclude-fields-without-expose-annotation= # Whether to exclude all fields from consideration for serialization or deserialization that do not have the "Expose" annotation.
spring.gson.field-naming-policy= # Naming policy that should be applied to an object's field during serialization and deserialization.
spring.gson.generate-non-executable-json= # Whether to generate non executable JSON by prefixing the output with some special text.
spring.gson.lenient= # Whether to be lenient about parsing JSON that doesn't conform to RFC 4627.
spring.gson.long-serialization-policy= # Serialization policy for Long and long types.
spring.gson.pretty-printing= # Whether to output serialized JSON that fits in a page for pretty printing.
spring.gson.serialize-nulls= # Whether to serialize null fields.
Spring Boot 中使用 Fastjson
國內很多的 Java 程序員更喜歡使用阿里的 fastjson 作為 json lib。那么,如何在 Spring Boot 中將其替換默認的 jackson 庫呢?
你需要做如下處理:
(1)引入 fastjson jar 包:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
(2)實現 WebMvcConfigurer 接口,自定義 configureMessageConverters
接口。如下所示:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* 自定義消息轉換器
* @param converters
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 清除默認 Json 轉換器
converters.removeIf(converter -> converter instanceof MappingJackson2HttpMessageConverter);
// 配置 FastJson
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(SerializerFeature.QuoteFieldNames, SerializerFeature.WriteEnumUsingToString,
SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat,
SerializerFeature.DisableCircularReferenceDetect);
// 添加 FastJsonHttpMessageConverter
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
fastJsonHttpMessageConverter.setFastJsonConfig(config);
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
converters.add(fastJsonHttpMessageConverter);
// 添加 StringHttpMessageConverter,解決中文亂碼問題
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
converters.add(stringHttpMessageConverter);
}
// ...
}
示例源碼
完整示例:源碼
引申和引用
引申
引用