Spring Boot中Jackson ObjectMapper应用详解

 

Spring Boot支持与三种JSON mapping库集成:Gson、Jackson和JSON-B。Jackson是首选和默认的。

Jackson是spring-boot-starter-json的一部分,spring-boot-starter-web中包含spring-boot-starter-json。也就是说,当项目中引入spring-boot-starter-web后会自动引入spring-boot-starter-json。

 
  1. org.springframework.boot

  2. spring-boot-starter-web

ObjectMapper

ObjectMapper是jackson-databind包中的一个类,提供读写JSON的功能,可以方便的进行对象和JSON转换:

 
  1. import com.fasterxml.jackson.core.JsonProcessingException;

  2. import com.fasterxml.jackson.databind.ObjectMapper;

  3.  
  4. import java.io.IOException;

  5.  
  6. public final class JsonUtil {

  7. private static ObjectMapper mapper = new ObjectMapper();

  8.  
  9. private JsonUtil() {

  10. }

  11.  
  12. /**

  13. * Serialize any Java value as a String.

  14. */

  15. public static String generate(Object object) throws JsonProcessingException {

  16. return mapper.writeValueAsString(object);

  17. }

  18.  
  19. /**

  20. * Deserialize JSON content from given JSON content String.

  21. */

  22. public static T parse(String content, Class valueType) throws IOException {

  23. return mapper.readValue(content, valueType);

  24. }

  25. }

编写一简单POJO测试类:

 
  1. import java.util.Date;

  2.  
  3. public class Hero {

  4.  
  5. public static void main(String[] args) throws Exception {

  6. System.out.println(JsonUtil.generate(new Hero("Jason", new Date())));

  7. }

  8.  
  9. private String name;

  10.  
  11. private Date birthday;

  12.  
  13. public Hero() {

  14. }

  15.  
  16. public Hero(String name, Date birthday) {

  17. this.name = name;

  18. this.birthday = birthday;

  19. }

  20.  
  21. public String getName() {

  22. return name;

  23. }

  24.  
  25. public Date getBirthday() {

  26. return birthday;

  27. }

  28. }

运行后输出结果如下:

{"name":"Jason","birthday":1540909420353}

可以看到日期转换为长整型。

ObjectMapper默认序列化配置启用了SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,日期将转换为Timestamp。可查看如下源码:

 
  1. public ObjectMapper(JsonFactory jf, DefaultSerializerProvider sp, DefaultDeserializationContext dc) {

  2. ...

  3. BaseSettings base = DEFAULT_BASE.withClassIntrospector(defaultClassIntrospector());

  4. _configOverrides = new ConfigOverrides();

  5. _serializationConfig = new SerializationConfig(base, _subtypeResolver, mixins, rootNames, _configOverrides);

  6. ...

  7. }

 
  1. public SerializationConfig(BaseSettings base, SubtypeResolver str, SimpleMixInResolver mixins, RootNameLookup rootNames,

  2. ConfigOverrides configOverrides)

  3. {

  4. super(base, str, mixins, rootNames, configOverrides);

  5. _serFeatures = collectFeatureDefaults(SerializationFeature.class);

  6. _filterProvider = null;

  7. _defaultPrettyPrinter = DEFAULT_PRETTY_PRINTER;

  8. _generatorFeatures = 0;

  9. _generatorFeaturesToChange = 0;

  10. _formatWriteFeatures = 0;

  11. _formatWriteFeaturesToChange = 0;

  12. }

默认情况下,Date类型序列化将调用DateSerializer的_timestamp 方法:

 
  1. /**

  2. * For efficiency, we will serialize Dates as longs, instead of

  3. * potentially more readable Strings.

  4. */

  5. @JacksonStdImpl

  6. @SuppressWarnings("serial")

  7. public class DateSerializer extends DateTimeSerializerBase {

  8. ...

  9.  
  10. @Override

  11. protected long _timestamp(Date value) {

  12. return (value == null) ? 0L : value.getTime();

  13. }

  14.  
  15. @Override

  16. public void serialize(Date value, JsonGenerator g, SerializerProvider provider) throws IOException {

  17. if (_asTimestamp(provider)) {

  18. g.writeNumber(_timestamp(value));

  19. return;

  20. }

  21. _serializeAsString(value, g, provider);

  22. }

  23. }

DateTimeSerializerBase的_asTimestamp方法:

 
  1. protected boolean _asTimestamp(SerializerProvider serializers)

  2. {

  3. if (_useTimestamp != null) {

  4. return _useTimestamp.booleanValue();

  5. }

  6. if (_customFormat == null) {

  7. if (serializers != null) {

  8. return serializers.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

  9. }

  10. // 12-Jun-2014, tatu: Is it legal not to have provider? Was NPE:ing earlier so leave a check

  11. throw new IllegalArgumentException("Null SerializerProvider passed for "+handledType().getName());

  12. }

  13. return false;

  14. }

禁用WRITE_DATES_AS_TIMESTAMPS

若要将日期序列化为字符串,可禁用SerializationFeature.WRITE_DATES_AS_TIMESTAMPS:

mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

这时序列化将调用StdDateFormat的format()方法,使用ISO-8601兼容格式"yyyy-MM-dd'T'HH:mm:ss.SSSZ",输出内容如下:

{"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注解,代替全局设置,是一种更灵活的方法:

 
  1. @JsonFormat(shape = JsonFormat.Shape.STRING)

  2. private Date birthday;

还可以定义pattern:

 
  1. @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")

  2. private Date birthday;

当自定义pattern后,将创建新的SimpleDateFormat实例来序列化日期,参见DateTimeSerializerBase的createContextual()方法:

 
  1. public JsonSerializer createContextual(SerializerProvider serializers, BeanProperty property) throws JsonMappingException

  2. {

  3. ...

  4. if (format.hasPattern()) {

  5. final Locale loc = format.hasLocale() ? format.getLocale() : serializers.getLocale();

  6. SimpleDateFormat df = new SimpleDateFormat(format.getPattern(), loc);

  7. TimeZone tz = format.hasTimeZone() ? format.getTimeZone() : serializers.getTimeZone();

  8. df.setTimeZone(tz);

  9. return withFormat(Boolean.FALSE, df);

  10. }

  11. ...

  12. }

Spring Boot与Jackson ObjectMapper

Spring Boot使用HttpMessageConverters处理HTTP交换中的内容转换。当classpath中存在Jackson时,Jackson2ObjectMapperBuilder将是默认的Converter,源码请查看HttpMessageConverters和WebMvcConfigurationSupport:
HttpMessageConverters

 
  1. private List> getDefaultConverters() {

  2. List> converters = new ArrayList<>();

  3. if (ClassUtils.isPresent("org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport", null)) {

  4. converters.addAll(new WebMvcConfigurationSupport() {

  5. public List> defaultMessageConverters() {

  6. return super.getMessageConverters();

  7. }

  8. }.defaultMessageConverters());

  9. }

  10. else {

  11. converters.addAll(new RestTemplate().getMessageConverters());

  12. }

  13. reorderXmlConvertersToEnd(converters);

  14. return converters;

  15. }

WebMvcConfigurationSupport

 
  1. protected final void addDefaultHttpMessageConverters(List> messageConverters) {

  2. StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();

  3. stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316

  4.  
  5. messageConverters.add(new ByteArrayHttpMessageConverter());

  6. messageConverters.add(stringHttpMessageConverter);

  7. messageConverters.add(new ResourceHttpMessageConverter());

  8. messageConverters.add(new ResourceRegionHttpMessageConverter());

  9. messageConverters.add(new SourceHttpMessageConverter<>());

  10. messageConverters.add(new AllEncompassingFormHttpMessageConverter());

  11.  
  12. ...

  13.  
  14. if (jackson2Present) {

  15. Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();

  16. if (this.applicationContext != null) {

  17. builder.applicationContext(this.applicationContext);

  18. }

  19. messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));

  20. }

  21. ...

  22. }

默认,Jackson2ObjectMapperBuilder将创建ObjectMapper实例:
Jackson2ObjectMapperBuilder

 
  1. public T build() {

  2. ObjectMapper mapper;

  3. if (this.createXmlMapper) {

  4. mapper = (this.defaultUseWrapper != null ?

  5. new XmlObjectMapperInitializer().create(this.defaultUseWrapper) :

  6. new XmlObjectMapperInitializer().create());

  7. }

  8. else {

  9. mapper = (this.factory != null ? new ObjectMapper(this.factory) : new ObjectMapper());

  10. }

  11. configure(mapper);

  12. return (T) mapper;

  13. }

ObjectMapper以下属性被禁用:

  • MapperFeature.DEFAULT_VIEW_INCLUSION
  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
  • SerializationFeature.WRITE_DATES_AS_TIMESTAMPS

Jackson2ObjectMapperBuilder

 
  1. private void customizeDefaultFeatures(ObjectMapper objectMapper) {

  2. if (!this.features.containsKey(MapperFeature.DEFAULT_VIEW_INCLUSION)) {

  3. configureFeature(objectMapper, MapperFeature.DEFAULT_VIEW_INCLUSION, false);

  4. }

  5. if (!this.features.containsKey(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {

  6. configureFeature(objectMapper, DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

  7. }

  8. }

JacksonAutoConfiguration

 
  1. static {

  2. Map featureDefaults = new HashMap<>();

  3. featureDefaults.put(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

  4. FEATURE_DEFAULTS = Collections.unmodifiableMap(featureDefaults);

  5. }

自定义Jackson ObjectMapper配置

针对ObjectMapper的六种Feature,Spring Boot都提供了相应的配置,列表如下:

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

例如,为启用美化打印,设置spring.jackson.serialization.indent_output=true,相当于启用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编程中,当提交application/json的POST/PUT请求时,JSON会通过Jackson进行转换。当提交GET请求时,如参数中包含日期,后台代码需要使用注解@DateTimeFormat:

 
  1. @DateTimeFormat(pattern = "yyyy-MM-dd")

  2. private Date startDate;

两者可以同时使用:

 
  1. @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")

  2. @DateTimeFormat(pattern = "yyyy-MM-dd")

  3. private Date startDate;

  4. 参考:

  5. https://blog.csdn.net/weixin_44048532/article/details/86647880

你可能感兴趣的:(问题解决,Java)