diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java index 627fdf52db..45c872d46c 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java @@ -172,6 +172,18 @@ public class Jackson2ObjectMapperBuilder { return this; } + /** + * Configure a custom serializer for the given type. + * @see #serializers(JsonSerializer...) + * @since 4.1.2 + */ + public Jackson2ObjectMapperBuilder serializerByType(Class type, JsonSerializer serializer) { + if (serializers != null) { + this.serializers.put(type, serializer); + } + return this; + } + /** * Configure custom serializers for the given types. * @see #serializers(JsonSerializer...) @@ -183,6 +195,17 @@ public class Jackson2ObjectMapperBuilder { return this; } + /** + * Configure a custom deserializer for the given type. + * @since 4.1.2 + */ + public Jackson2ObjectMapperBuilder deserializerByType(Class type, JsonDeserializer deserializer) { + if (deserializers != null) { + this.deserializers.put(type, deserializer); + } + return this; + } + /** * Configure custom deserializers for the given types. */ @@ -193,6 +216,21 @@ public class Jackson2ObjectMapperBuilder { return this; } + /** + * Add mix-in annotations to use for augmenting specified class or interface. + * @param target class (or interface) whose annotations to effectively override + * @param mixinSource class (or interface) whose annotations are to be "added" + * to target's annotations as value + * @since 4.1.2 + * @see com.fasterxml.jackson.databind.ObjectMapper#addMixInAnnotations(Class, Class) + */ + public Jackson2ObjectMapperBuilder mixIn(Class target, Class mixinSource) { + if (mixIns != null) { + this.mixIns.put(target, mixinSource); + } + return this; + } + /** * Add mix-in annotations to use for augmenting specified class or interface. * @param mixIns Map of entries with target classes (or interface) whose annotations diff --git a/spring-web/src/test/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilderTests.java b/spring-web/src/test/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilderTests.java index 2be4732970..4fcc86dfba 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilderTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilderTests.java @@ -28,6 +28,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.Module; @@ -37,6 +38,7 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig; import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig; import com.fasterxml.jackson.databind.deser.BasicDeserializerFactory; +import com.fasterxml.jackson.databind.deser.Deserializers; import com.fasterxml.jackson.databind.deser.std.DateDeserializers; import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector; import com.fasterxml.jackson.databind.module.SimpleModule; @@ -190,46 +192,76 @@ public class Jackson2ObjectMapperBuilderTests { assertSame(strategy, objectMapper.getDeserializationConfig().getPropertyNamingStrategy()); } + @Test + public void serializerByType() { + JsonSerializer serializer = new NumberSerializer(); + ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json() + .serializerByType(Boolean.class, serializer).build(); + assertTrue(getSerializerFactoryConfig(objectMapper).hasSerializers()); + Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next(); + assertTrue(serializers.findSerializer(null, SimpleType.construct(Boolean.class), null) == serializer); + } + + @Test + public void deserializerByType() throws JsonMappingException { + JsonDeserializer deserializer = new DateDeserializers.DateDeserializer(); + ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json() + .deserializerByType(Date.class, deserializer).build(); + assertTrue(getDeserializerFactoryConfig(objectMapper).hasDeserializers()); + Deserializers deserializers = getDeserializerFactoryConfig(objectMapper).deserializers().iterator().next(); + assertTrue(deserializers.findBeanDeserializer(SimpleType.construct(Date.class), null, null) == deserializer); + } + + @Test + public void mixIn() { + Class target = String.class; + Class mixInSource = Object.class; + + ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().mixIn(target, + mixInSource).build(); + + assertEquals(1, objectMapper.mixInCount()); + assertSame(mixInSource, objectMapper.findMixInClassFor(target)); + } + @Test public void mixIns() { Class target = String.class; - Class mixinSource = Object.class; + Class mixInSource = Object.class; Map, Class> mixIns = new HashMap, Class>(); - mixIns.put(target, mixinSource); + mixIns.put(target, mixInSource); ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().mixIns(mixIns).build(); assertEquals(1, objectMapper.mixInCount()); - assertSame(mixinSource, objectMapper.findMixInClassFor(target)); + assertSame(mixInSource, objectMapper.findMixInClassFor(target)); } @Test - public void completeSetup() { + public void completeSetup() throws JsonMappingException { NopAnnotationIntrospector annotationIntrospector = NopAnnotationIntrospector.instance; - Map, JsonDeserializer> deserializers = new HashMap, JsonDeserializer>(); - deserializers.put(Date.class, new DateDeserializers.DateDeserializer()); + Map, JsonDeserializer> deserializerMap = new HashMap, JsonDeserializer>(); + JsonDeserializer deserializer = new DateDeserializers.DateDeserializer(); + deserializerMap.put(Date.class, deserializer); JsonSerializer> serializer1 = new ClassSerializer(); JsonSerializer serializer2 = new NumberSerializer(); - Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json(); - - builder.serializers(serializer1); - builder.serializersByType(Collections., JsonSerializer>singletonMap(Boolean.class, serializer2)); - builder.deserializersByType(deserializers); - builder.annotationIntrospector(annotationIntrospector); - - builder.featuresToEnable(SerializationFeature.FAIL_ON_EMPTY_BEANS, - DeserializationFeature.UNWRAP_ROOT_VALUE, - JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, - JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS); - - builder.featuresToDisable(MapperFeature.AUTO_DETECT_GETTERS, - MapperFeature.AUTO_DETECT_FIELDS, JsonParser.Feature.AUTO_CLOSE_SOURCE, - JsonGenerator.Feature.QUOTE_FIELD_NAMES); - - builder.serializationInclusion(JsonInclude.Include.NON_NULL); + Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json() + .serializers(serializer1) + .serializersByType(Collections., JsonSerializer>singletonMap(Boolean.class, serializer2)) + .deserializersByType(deserializerMap) + .annotationIntrospector(annotationIntrospector) + .featuresToEnable(SerializationFeature.FAIL_ON_EMPTY_BEANS, + DeserializationFeature.UNWRAP_ROOT_VALUE, + JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, + JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS) + .featuresToDisable(MapperFeature.AUTO_DETECT_GETTERS, + MapperFeature.AUTO_DETECT_FIELDS, + JsonParser.Feature.AUTO_CLOSE_SOURCE, + JsonGenerator.Feature.QUOTE_FIELD_NAMES) + .serializationInclusion(JsonInclude.Include.NON_NULL); ObjectMapper objectMapper = new ObjectMapper(); builder.configure(objectMapper); @@ -242,6 +274,9 @@ public class Jackson2ObjectMapperBuilderTests { assertTrue(serializers.findSerializer(null, SimpleType.construct(Boolean.class), null) == serializer2); assertNull(serializers.findSerializer(null, SimpleType.construct(Number.class), null)); + Deserializers deserializers = getDeserializerFactoryConfig(objectMapper).deserializers().iterator().next(); + assertTrue(deserializers.findBeanDeserializer(SimpleType.construct(Date.class), null, null) == deserializer); + assertTrue(annotationIntrospector == objectMapper.getSerializationConfig().getAnnotationIntrospector()); assertTrue(annotationIntrospector == objectMapper.getDeserializationConfig().getAnnotationIntrospector());