SpringBoot中Jackson ObjectMapper應(yīng)用及說明
Spring Boot支持與三種JSON mapping庫集成:Gson、Jackson和JSON-B。
Jackson是首選和默認的。
Jackson是spring-boot-starter-json的一部分,spring-boot-starter-web中包含spring-boot-starter-json。也就是說,當(dāng)項目中引入spring-boot-starter-web后會自動引入spring-boot-starter-json。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>ObjectMapper
ObjectMapper是jackson-databind包中的一個類,提供讀寫JSON的功能,可以方便的進行對象和JSON轉(zhuǎn)換:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public final class JsonUtil {
private static ObjectMapper mapper = new ObjectMapper();
private JsonUtil() {
}
/**
* Serialize any Java value as a String.
*/
public static String generate(Object object) throws JsonProcessingException {
return mapper.writeValueAsString(object);
}
/**
* Deserialize JSON content from given JSON content String.
*/
public static <T> T parse(String content, Class<T> valueType) throws IOException {
return mapper.readValue(content, valueType);
}
}編寫一簡單POJO測試類:
import java.util.Date;
public class Hero {
public static void main(String[] args) throws Exception {
System.out.println(JsonUtil.generate(new Hero("Jason", new Date())));
}
private String name;
private Date birthday;
public Hero() {
}
public Hero(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public String getName() {
return name;
}
public Date getBirthday() {
return birthday;
}
}運行后輸出結(jié)果如下:
{"name":"Jason","birthday":1540909420353}
可以看到日期轉(zhuǎn)換為長整型。
ObjectMapper默認序列化配置啟用了SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,日期將轉(zhuǎn)換為Timestamp。
可查看如下源碼:
public ObjectMapper(JsonFactory jf, DefaultSerializerProvider sp, DefaultDeserializationContext dc) {
...
BaseSettings base = DEFAULT_BASE.withClassIntrospector(defaultClassIntrospector());
_configOverrides = new ConfigOverrides();
_serializationConfig = new SerializationConfig(base, _subtypeResolver, mixins, rootNames, _configOverrides);
...
}public SerializationConfig(BaseSettings base, SubtypeResolver str, SimpleMixInResolver mixins, RootNameLookup rootNames,
ConfigOverrides configOverrides)
{
super(base, str, mixins, rootNames, configOverrides);
_serFeatures = collectFeatureDefaults(SerializationFeature.class);
_filterProvider = null;
_defaultPrettyPrinter = DEFAULT_PRETTY_PRINTER;
_generatorFeatures = 0;
_generatorFeaturesToChange = 0;
_formatWriteFeatures = 0;
_formatWriteFeaturesToChange = 0;
}默認情況下,Date類型序列化將調(diào)用DateSerializer的_timestamp 方法:
/**
* For efficiency, we will serialize Dates as longs, instead of
* potentially more readable Strings.
*/
@JacksonStdImpl
@SuppressWarnings("serial")
public class DateSerializer extends DateTimeSerializerBase<Date> {
...
@Override
protected long _timestamp(Date value) {
return (value == null) ? 0L : value.getTime();
}
@Override
public void serialize(Date value, JsonGenerator g, SerializerProvider provider) throws IOException {
if (_asTimestamp(provider)) {
g.writeNumber(_timestamp(value));
return;
}
_serializeAsString(value, g, provider);
}
}DateTimeSerializerBase的_asTimestamp方法:
protected boolean _asTimestamp(SerializerProvider serializers)
{
if (_useTimestamp != null) {
return _useTimestamp.booleanValue();
}
if (_customFormat == null) {
if (serializers != null) {
return serializers.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
// 12-Jun-2014, tatu: Is it legal not to have provider? Was NPE:ing earlier so leave a check
throw new IllegalArgumentException("Null SerializerProvider passed for "+handledType().getName());
}
return false;
}禁用WRITE_DATES_AS_TIMESTAMPS
若要將日期序列化為字符串,可禁用SerializationFeature.WRITE_DATES_AS_TIMESTAMPS:
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
這時序列化將調(diào)用StdDateFormat的format()方法,使用ISO-8601兼容格式"yyyy-MM-dd'T'HH:mm:ss.SSSZ",輸出內(nèi)容如下:
{"name":"Jason","birthday":"2018-10-31T03:07:34.485+0000"}StdDateFormat反序列化支持ISO-8601兼容格式和RFC-1123("EEE, dd MMM yyyy HH:mm:ss zzz")格式。
@JsonFormat
使用@JsonFormat注解,代替全局設(shè)置,是一種更靈活的方法:
@JsonFormat(shape = JsonFormat.Shape.STRING) private Date birthday;
還可以定義pattern:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") private Date birthday;
當(dāng)自定義pattern后,將創(chuàng)建新的SimpleDateFormat實例來序列化日期,參見DateTimeSerializerBase的createContextual()方法:
public JsonSerializer<?> createContextual(SerializerProvider serializers, BeanProperty property) throws JsonMappingException
{
...
if (format.hasPattern()) {
final Locale loc = format.hasLocale() ? format.getLocale() : serializers.getLocale();
SimpleDateFormat df = new SimpleDateFormat(format.getPattern(), loc);
TimeZone tz = format.hasTimeZone() ? format.getTimeZone() : serializers.getTimeZone();
df.setTimeZone(tz);
return withFormat(Boolean.FALSE, df);
}
...
}Spring Boot與Jackson ObjectMapper
Spring Boot使用HttpMessageConverters處理HTTP交換中的內(nèi)容轉(zhuǎn)換。
當(dāng)classpath中存在Jackson時,Jackson2ObjectMapperBuilder將是默認的Converter,源碼請查看HttpMessageConverters和WebMvcConfigurationSupport:
HttpMessageConverters
private List<HttpMessageConverter<?>> getDefaultConverters() {
List<HttpMessageConverter<?>> converters = new ArrayList<>();
if (ClassUtils.isPresent("org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport", null)) {
converters.addAll(new WebMvcConfigurationSupport() {
public List<HttpMessageConverter<?>> defaultMessageConverters() {
return super.getMessageConverters();
}
}.defaultMessageConverters());
}
else {
converters.addAll(new RestTemplate().getMessageConverters());
}
reorderXmlConvertersToEnd(converters);
return converters;
}WebMvcConfigurationSupport
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(stringHttpMessageConverter);
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new ResourceRegionHttpMessageConverter());
messageConverters.add(new SourceHttpMessageConverter<>());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
...
if (jackson2Present) {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
...
}默認,Jackson2ObjectMapperBuilder將創(chuàng)建ObjectMapper實例:
Jackson2ObjectMapperBuilder
public <T extends ObjectMapper> T build() {
ObjectMapper mapper;
if (this.createXmlMapper) {
mapper = (this.defaultUseWrapper != null ?
new XmlObjectMapperInitializer().create(this.defaultUseWrapper) :
new XmlObjectMapperInitializer().create());
}
else {
mapper = (this.factory != null ? new ObjectMapper(this.factory) : new ObjectMapper());
}
configure(mapper);
return (T) mapper;
}ObjectMapper以下屬性被禁用:
- MapperFeature.DEFAULT_VIEW_INCLUSION
- DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
- SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
Jackson2ObjectMapperBuilder
private void customizeDefaultFeatures(ObjectMapper objectMapper) {
if (!this.features.containsKey(MapperFeature.DEFAULT_VIEW_INCLUSION)) {
configureFeature(objectMapper, MapperFeature.DEFAULT_VIEW_INCLUSION, false);
}
if (!this.features.containsKey(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
configureFeature(objectMapper, DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
}JacksonAutoConfiguration
static {
Map<Object, Boolean> featureDefaults = new HashMap<>();
featureDefaults.put(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
FEATURE_DEFAULTS = Collections.unmodifiableMap(featureDefaults);
}自定義Jackson ObjectMapper配置
針對ObjectMapper的六種Feature,Spring Boot都提供了相應(yīng)的配置,列表如下:
| Feature(Enum) | Spring Boot Property | Values |
|---|---|---|
| com.fasterxml.jackson.databind.DeserializationFeature | spring.jackson.deserialization.feature_name | true, false |
| com.fasterxml.jackson.core.JsonGenerator.Feature | spring.jackson.generator.feature_name | true, false |
| com.fasterxml.jackson.databind.MapperFeature | spring.jackson.mapper.feature_name | true, false |
| com.fasterxml.jackson.core.JsonParser.Feature | spring.jackson.parser.feature_name | true, false |
| com.fasterxml.jackson.databind.SerializationFeature | spring.jackson.serialization.feature_name | true, false |
| com.fasterxml.jackson.annotation.JsonInclude.Include | spring.jackson.default-property-inclusion | always, non_null, non_absent, non_default, non_empty |
例如,為啟用美化打印,設(shè)置spring.jackson.serialization.indent_output=true,相當(dāng)于啟用SerializationFeature.INDENT_OUTPUT,配置中忽略feature_name大小寫。
其他的Jackson配置屬性:
- 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.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.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.time-zone= # Time zone used when formatting dates. For instance, "America/Los_Angeles" or "GMT+10".
其中spring.jackson.date-format默認值為com.fasterxml.jackson.databind.util.StdDateFormat。
@DateTimeFormat和@JsonFormat
在REST編程中,當(dāng)提交application/json的POST/PUT請求時,JSON會通過Jackson進行轉(zhuǎn)換。
當(dāng)提交GET請求時,如參數(shù)中包含日期,后臺代碼需要使用注解@DateTimeFormat:
@DateTimeFormat(pattern = "yyyy-MM-dd") private Date startDate;
兩者可以同時使用:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") @DateTimeFormat(pattern = "yyyy-MM-dd") private Date startDate;
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot啟動時運行特定代碼的多種方式小結(jié)
SpringBoot提供了多種方式在應(yīng)用程序啟動時運行特定的代碼,包括CommandLineRunner、ApplicationRunner、@PostConstruct、InitializingBean、事件機制和自定義注解等,下面就來具體介紹一下2025-01-01
簡單了解Java關(guān)鍵字throw和throws的區(qū)別
這篇文章主要介紹了簡單了解Java關(guān)鍵字throw和throws的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-11-11
java中volatile關(guān)鍵字的作用與實例代碼
這篇文章主要給大家介紹了關(guān)于java中volatile關(guān)鍵字的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
零基礎(chǔ)寫Java知乎爬蟲之將抓取的內(nèi)容存儲到本地
上一回我們說到了如何把知乎的某些內(nèi)容爬取出來,那么這一回我們就說說怎么把這些內(nèi)容存儲到本地吧。2014-11-11
Java基礎(chǔ) Servlet監(jiān)聽器詳解
這篇文章主要介紹了Java基礎(chǔ) Servlet監(jiān)聽器詳解的相關(guān)資料,需要的朋友可以參考下2017-07-07
Java將一個正整數(shù)分解質(zhì)因數(shù)的代碼
這篇文章主要介紹了將一個正整數(shù)分解質(zhì)因數(shù)。例如:輸入90,打印出90=2*3*3*5,需要的朋友可以參考下2017-02-02
SpringCloud微服務(wù) Sentinel 實戰(zhàn)指南
文章主要介紹了Sentletinel在微服務(wù)中的應(yīng)用及工作原理,涵蓋了流控規(guī)則、熔斷降級、授權(quán)控制等內(nèi)容,強調(diào)了通過合理的限流和降級策略來保護系統(tǒng)穩(wěn)定運行,感興趣的朋友跟隨小編一起看看吧2026-05-05

