@ -82,9 +82,10 @@ import org.springframework.util.TypeUtils;
*
*
* @author Sebastien Deleuze
* @author Sebastien Deleuze
* @since 7 . 0
* @since 7 . 0
* @param < T > the type of { @link ObjectMapper }
* @see JacksonJsonHttpMessageConverter
* @see JacksonJsonHttpMessageConverter
* /
* /
public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartHttpMessageConverter < Object > {
public abstract class AbstractJacksonHttpMessageConverter < T extends ObjectMapper > extends AbstractSmartHttpMessageConverter < Object > {
private static final String JSON_VIEW_HINT = JsonView . class . getName ( ) ;
private static final String JSON_VIEW_HINT = JsonView . class . getName ( ) ;
@ -103,9 +104,9 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
}
}
protected final ObjectMapper defaultObjec tMapper;
protected final T defaul tMapper;
private @Nullable Map < Class < ? > , Map < MediaType , ObjectMapper > > objectM apperRegistrations;
private @Nullable Map < Class < ? > , Map < MediaType , T > > m apperRegistrations;
private final @Nullable PrettyPrinter ssePrettyPrinter ;
private final @Nullable PrettyPrinter ssePrettyPrinter ;
@ -115,8 +116,8 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
* customized with the { @link tools . jackson . databind . JacksonModule } s found
* customized with the { @link tools . jackson . databind . JacksonModule } s found
* by { @link MapperBuilder # findModules ( ClassLoader ) } .
* by { @link MapperBuilder # findModules ( ClassLoader ) } .
* /
* /
private AbstractJacksonHttpMessageConverter ( MapperBuilder < ? , ? > builder ) {
private AbstractJacksonHttpMessageConverter ( MapperBuilder < T , ? > builder ) {
this . defaultObject Mapper = builder . addModules ( initModules ( ) ) . build ( ) ;
this . defaultMapper = builder . addModules ( initModules ( ) ) . build ( ) ;
this . ssePrettyPrinter = initSsePrettyPrinter ( ) ;
this . ssePrettyPrinter = initSsePrettyPrinter ( ) ;
}
}
@ -125,7 +126,7 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
* customized with the { @link tools . jackson . databind . JacksonModule } s found
* customized with the { @link tools . jackson . databind . JacksonModule } s found
* by { @link MapperBuilder # findModules ( ClassLoader ) } and { @link MediaType } .
* by { @link MapperBuilder # findModules ( ClassLoader ) } and { @link MediaType } .
* /
* /
protected AbstractJacksonHttpMessageConverter ( MapperBuilder < ? , ? > builder , MediaType supportedMediaType ) {
protected AbstractJacksonHttpMessageConverter ( MapperBuilder < T , ? > builder , MediaType supportedMediaType ) {
this ( builder ) ;
this ( builder ) ;
setSupportedMediaTypes ( Collections . singletonList ( supportedMediaType ) ) ;
setSupportedMediaTypes ( Collections . singletonList ( supportedMediaType ) ) ;
}
}
@ -135,7 +136,7 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
* customized with the { @link tools . jackson . databind . JacksonModule } s found
* customized with the { @link tools . jackson . databind . JacksonModule } s found
* by { @link MapperBuilder # findModules ( ClassLoader ) } and { @link MediaType } s .
* by { @link MapperBuilder # findModules ( ClassLoader ) } and { @link MediaType } s .
* /
* /
protected AbstractJacksonHttpMessageConverter ( MapperBuilder < ? , ? > builder , MediaType . . . supportedMediaTypes ) {
protected AbstractJacksonHttpMessageConverter ( MapperBuilder < T , ? > builder , MediaType . . . supportedMediaTypes ) {
this ( builder ) ;
this ( builder ) ;
setSupportedMediaTypes ( Arrays . asList ( supportedMediaTypes ) ) ;
setSupportedMediaTypes ( Arrays . asList ( supportedMediaTypes ) ) ;
}
}
@ -143,24 +144,24 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
/ * *
/ * *
* Construct a new instance with the provided { @link ObjectMapper } .
* Construct a new instance with the provided { @link ObjectMapper } .
* /
* /
protected AbstractJacksonHttpMessageConverter ( ObjectMapper objectM apper) {
protected AbstractJacksonHttpMessageConverter ( T m apper) {
this . defaultObject Mapper = objectM apper;
this . defaultMapper = m apper;
this . ssePrettyPrinter = initSsePrettyPrinter ( ) ;
this . ssePrettyPrinter = initSsePrettyPrinter ( ) ;
}
}
/ * *
/ * *
* Construct a new instance with the provided { @link ObjectMapper } and { @link MediaType } .
* Construct a new instance with the provided { @link ObjectMapper } and { @link MediaType } .
* /
* /
protected AbstractJacksonHttpMessageConverter ( ObjectMapper objectM apper, MediaType supportedMediaType ) {
protected AbstractJacksonHttpMessageConverter ( T m apper, MediaType supportedMediaType ) {
this ( objectM apper) ;
this ( m apper) ;
setSupportedMediaTypes ( Collections . singletonList ( supportedMediaType ) ) ;
setSupportedMediaTypes ( Collections . singletonList ( supportedMediaType ) ) ;
}
}
/ * *
/ * *
* Construct a new instance with the provided { @link ObjectMapper } and { @link MediaType } s .
* Construct a new instance with the provided { @link ObjectMapper } and { @link MediaType } s .
* /
* /
protected AbstractJacksonHttpMessageConverter ( ObjectMapper objectM apper, MediaType . . . supportedMediaTypes ) {
protected AbstractJacksonHttpMessageConverter ( T m apper, MediaType . . . supportedMediaTypes ) {
this ( objectM apper) ;
this ( m apper) ;
setSupportedMediaTypes ( Arrays . asList ( supportedMediaTypes ) ) ;
setSupportedMediaTypes ( Arrays . asList ( supportedMediaTypes ) ) ;
}
}
@ -184,19 +185,19 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
}
}
/ * *
/ * *
* Return the main { @code ObjectMapper } in use .
* Return the main { @link ObjectMapper } in use .
* /
* /
public ObjectMapper getObjec tMapper( ) {
public T ge tMapper( ) {
return this . defaultObject Mapper ;
return this . defaultMapper ;
}
}
/ * *
/ * *
* Configure the { @link ObjectMapper } instances to use for the given
* Configure the { @link ObjectMapper } instances to use for the given
* { @link Class } . This is useful when you want to deviate from the
* { @link Class } . This is useful when you want to deviate from the
* { @link # getObject Mapper ( ) default } ObjectMapper or have the
* { @link # getMapper ( ) default } ObjectMapper or have the
* { @code ObjectMapper } vary by { @code MediaType } .
* { @code ObjectMapper } vary by { @code MediaType } .
* < p > < strong > Note : < / strong > Use of this method effectively turns off use of
* < p > < strong > Note : < / strong > Use of this method effectively turns off use of
* the default { @link # getObject Mapper ( ) ObjectMapper } and
* the default { @link # getMapper ( ) ObjectMapper } and
* { @link # setSupportedMediaTypes ( List ) supportedMediaTypes } for the given
* { @link # setSupportedMediaTypes ( List ) supportedMediaTypes } for the given
* class . Therefore it is important for the mappings configured here to
* class . Therefore it is important for the mappings configured here to
* { @link MediaType # includes ( MediaType ) include } every MediaType that must
* { @link MediaType # includes ( MediaType ) include } every MediaType that must
@ -205,12 +206,12 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
* @param registrar a consumer to populate or otherwise update the
* @param registrar a consumer to populate or otherwise update the
* MediaType - to - ObjectMapper associations for the given Class
* MediaType - to - ObjectMapper associations for the given Class
* /
* /
public void registerObject MappersForType ( Class < ? > clazz , Consumer < Map < MediaType , ObjectMapper > > registrar ) {
public void registerMappersForType ( Class < ? > clazz , Consumer < Map < MediaType , T > > registrar ) {
if ( this . objectM apperRegistrations = = null ) {
if ( this . m apperRegistrations = = null ) {
this . objectM apperRegistrations = new LinkedHashMap < > ( ) ;
this . m apperRegistrations = new LinkedHashMap < > ( ) ;
}
}
Map < MediaType , ObjectMapper > registrations =
Map < MediaType , T > registrations =
this . objectM apperRegistrations. computeIfAbsent ( clazz , c - > new LinkedHashMap < > ( ) ) ;
this . m apperRegistrations. computeIfAbsent ( clazz , c - > new LinkedHashMap < > ( ) ) ;
registrar . accept ( registrations ) ;
registrar . accept ( registrations ) ;
}
}
@ -220,8 +221,8 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
* @return a map with registered MediaType - to - ObjectMapper registrations ,
* @return a map with registered MediaType - to - ObjectMapper registrations ,
* or empty if in case of no registrations for the given class .
* or empty if in case of no registrations for the given class .
* /
* /
public Map < MediaType , ObjectMapper > getObjec tMappersForType ( Class < ? > clazz ) {
public Map < MediaType , T > getMappersForType ( Class < ? > clazz ) {
for ( Map . Entry < Class < ? > , Map < MediaType , ObjectMapper > > entry : getObjec tMapperRegistrations ( ) . entrySet ( ) ) {
for ( Map . Entry < Class < ? > , Map < MediaType , T > > entry : getMapperRegistrations ( ) . entrySet ( ) ) {
if ( entry . getKey ( ) . isAssignableFrom ( clazz ) ) {
if ( entry . getKey ( ) . isAssignableFrom ( clazz ) ) {
return entry . getValue ( ) ;
return entry . getValue ( ) ;
}
}
@ -232,7 +233,7 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
@Override
@Override
public List < MediaType > getSupportedMediaTypes ( Class < ? > clazz ) {
public List < MediaType > getSupportedMediaTypes ( Class < ? > clazz ) {
List < MediaType > result = null ;
List < MediaType > result = null ;
for ( Map . Entry < Class < ? > , Map < MediaType , ObjectMapper > > entry : getObjec tMapperRegistrations ( ) . entrySet ( ) ) {
for ( Map . Entry < Class < ? > , Map < MediaType , T > > entry : getMapperRegistrations ( ) . entrySet ( ) ) {
if ( entry . getKey ( ) . isAssignableFrom ( clazz ) ) {
if ( entry . getKey ( ) . isAssignableFrom ( clazz ) ) {
result = ( result ! = null ? result : new ArrayList < > ( entry . getValue ( ) . size ( ) ) ) ;
result = ( result ! = null ? result : new ArrayList < > ( entry . getValue ( ) . size ( ) ) ) ;
result . addAll ( entry . getValue ( ) . keySet ( ) ) ;
result . addAll ( entry . getValue ( ) . keySet ( ) ) ;
@ -245,8 +246,8 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
getMediaTypesForProblemDetail ( ) : getSupportedMediaTypes ( ) ) ;
getMediaTypesForProblemDetail ( ) : getSupportedMediaTypes ( ) ) ;
}
}
private Map < Class < ? > , Map < MediaType , ObjectMapper > > getObjec tMapperRegistrations ( ) {
private Map < Class < ? > , Map < MediaType , T > > getMapperRegistrations ( ) {
return ( this . objectM apperRegistrations ! = null ? this . objectM apperRegistrations : Collections . emptyMap ( ) ) ;
return ( this . m apperRegistrations ! = null ? this . m apperRegistrations : Collections . emptyMap ( ) ) ;
}
}
/ * *
/ * *
@ -267,7 +268,7 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
if ( clazz = = null ) {
if ( clazz = = null ) {
return false ;
return false ;
}
}
return this . objectM apperRegistrations = = null | | selectObj ectMapper ( clazz , mediaType ) ! = null ;
return this . m apperRegistrations = = null | | selectMapper ( clazz , mediaType ) ! = null ;
}
}
@Override
@Override
@ -285,23 +286,23 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
if ( MappingJacksonValue . class . isAssignableFrom ( clazz ) ) {
if ( MappingJacksonValue . class . isAssignableFrom ( clazz ) ) {
throw new UnsupportedOperationException ( "MappingJacksonValue is not supported, use hints instead" ) ;
throw new UnsupportedOperationException ( "MappingJacksonValue is not supported, use hints instead" ) ;
}
}
return this . objectM apperRegistrations = = null | | selectObj ectMapper ( clazz , mediaType ) ! = null ;
return this . m apperRegistrations = = null | | selectMapper ( clazz , mediaType ) ! = null ;
}
}
/ * *
/ * *
* Select an ObjectMapper to use , either the main ObjectMapper or another
* Select an ObjectMapper to use , either the main ObjectMapper or another
* if the handling for the given Class has been customized through
* if the handling for the given Class has been customized through
* { @link # registerObject MappersForType ( Class , Consumer ) } .
* { @link # registerMappersForType ( Class , Consumer ) } .
* /
* /
private @Nullable ObjectMapper selectObj ectMapper( Class < ? > targetType , @Nullable MediaType targetMediaType ) {
private @Nullable T sel ectMapper( Class < ? > targetType , @Nullable MediaType targetMediaType ) {
if ( targetMediaType = = null | | CollectionUtils . isEmpty ( this . objectM apperRegistrations) ) {
if ( targetMediaType = = null | | CollectionUtils . isEmpty ( this . m apperRegistrations) ) {
return this . defaultObject Mapper ;
return this . defaultMapper ;
}
}
for ( Map . Entry < Class < ? > , Map < MediaType , ObjectMapper > > typeEntry : getObjec tMapperRegistrations ( ) . entrySet ( ) ) {
for ( Map . Entry < Class < ? > , Map < MediaType , T > > typeEntry : getMapperRegistrations ( ) . entrySet ( ) ) {
if ( typeEntry . getKey ( ) . isAssignableFrom ( targetType ) ) {
if ( typeEntry . getKey ( ) . isAssignableFrom ( targetType ) ) {
for ( Map . Entry < MediaType , ObjectMapper > objectM apperEntry : typeEntry . getValue ( ) . entrySet ( ) ) {
for ( Map . Entry < MediaType , T > m apperEntry : typeEntry . getValue ( ) . entrySet ( ) ) {
if ( objectM apperEntry. getKey ( ) . includes ( targetMediaType ) ) {
if ( m apperEntry. getKey ( ) . includes ( targetMediaType ) ) {
return objectM apperEntry. getValue ( ) ;
return m apperEntry. getValue ( ) ;
}
}
}
}
// No matching registrations
// No matching registrations
@ -309,7 +310,7 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
}
}
}
}
// No registrations
// No registrations
return this . defaultObject Mapper ;
return this . defaultMapper ;
}
}
@Override
@Override
@ -334,8 +335,8 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
MediaType contentType = inputMessage . getHeaders ( ) . getContentType ( ) ;
MediaType contentType = inputMessage . getHeaders ( ) . getContentType ( ) ;
Charset charset = getCharset ( contentType ) ;
Charset charset = getCharset ( contentType ) ;
ObjectMapper objectM apper = selectObj ectMapper ( javaType . getRawClass ( ) , contentType ) ;
T m apper = selectMapper ( javaType . getRawClass ( ) , contentType ) ;
Assert . state ( objectM apper ! = null , ( ) - > "No ObjectMapper for " + javaType ) ;
Assert . state ( m apper ! = null , ( ) - > "No ObjectMapper for " + javaType ) ;
boolean isUnicode = ENCODINGS . containsKey ( charset . name ( ) ) | |
boolean isUnicode = ENCODINGS . containsKey ( charset . name ( ) ) | |
"UTF-16" . equals ( charset . name ( ) ) | |
"UTF-16" . equals ( charset . name ( ) ) | |
@ -345,7 +346,7 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
if ( inputMessage instanceof MappingJacksonInputMessage ) {
if ( inputMessage instanceof MappingJacksonInputMessage ) {
throw new UnsupportedOperationException ( "MappingJacksonInputMessage is not supported, use hints instead" ) ;
throw new UnsupportedOperationException ( "MappingJacksonInputMessage is not supported, use hints instead" ) ;
}
}
ObjectReader objectReader = objectM apper. readerFor ( javaType ) ;
ObjectReader objectReader = m apper. readerFor ( javaType ) ;
if ( hints ! = null & & hints . containsKey ( JSON_VIEW_HINT ) ) {
if ( hints ! = null & & hints . containsKey ( JSON_VIEW_HINT ) ) {
objectReader = objectReader . withView ( ( Class < ? > ) hints . get ( JSON_VIEW_HINT ) ) ;
objectReader = objectReader . withView ( ( Class < ? > ) hints . get ( JSON_VIEW_HINT ) ) ;
}
}
@ -401,8 +402,8 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
JsonEncoding encoding = getJsonEncoding ( contentType ) ;
JsonEncoding encoding = getJsonEncoding ( contentType ) ;
Class < ? > clazz = object . getClass ( ) ;
Class < ? > clazz = object . getClass ( ) ;
ObjectMapper objectM apper = selectObj ectMapper ( clazz , contentType ) ;
T m apper = selectMapper ( clazz , contentType ) ;
Assert . state ( objectM apper ! = null , ( ) - > "No ObjectMapper for " + clazz . getName ( ) ) ;
Assert . state ( m apper ! = null , ( ) - > "No ObjectMapper for " + clazz . getName ( ) ) ;
OutputStream outputStream = StreamUtils . nonClosing ( outputMessage . getBody ( ) ) ;
OutputStream outputStream = StreamUtils . nonClosing ( outputMessage . getBody ( ) ) ;
Class < ? > jsonView = null ;
Class < ? > jsonView = null ;
@ -419,7 +420,7 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
}
}
ObjectWriter objectWriter = ( jsonView ! = null ?
ObjectWriter objectWriter = ( jsonView ! = null ?
objectM apper. writerWithView ( jsonView ) : objectM apper. writer ( ) ) ;
m apper. writerWithView ( jsonView ) : m apper. writer ( ) ) ;
if ( filters ! = null ) {
if ( filters ! = null ) {
objectWriter = objectWriter . with ( filters ) ;
objectWriter = objectWriter . with ( filters ) ;
}
}
@ -485,7 +486,7 @@ public abstract class AbstractJacksonHttpMessageConverter extends AbstractSmartH
* @return the Jackson JavaType
* @return the Jackson JavaType
* /
* /
protected JavaType getJavaType ( Type type , @Nullable Class < ? > contextClass ) {
protected JavaType getJavaType ( Type type , @Nullable Class < ? > contextClass ) {
return this . defaultObject Mapper . constructType ( GenericTypeResolver . resolveType ( type , contextClass ) ) ;
return this . defaultMapper . constructType ( GenericTypeResolver . resolveType ( type , contextClass ) ) ;
}
}
/ * *
/ * *