Add builder-based constructors to Jackson 3 infrastructure

This commit also polishes documentation, constructor parameter and
field names.

Closes gh-35597
This commit is contained in:
Sébastien Deleuze
2025-10-15 11:49:55 +02:00
parent 3493fc9324
commit 8ec9ab6698
17 changed files with 325 additions and 119 deletions
@@ -48,9 +48,6 @@ import org.springframework.util.ClassUtils;
* {@link #setTargetType targetType} is set to {@link MessageType#TEXT}.
* Converts from a {@link TextMessage} or {@link BytesMessage} to an object.
*
* <p>The default constructor loads {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
*
* @author Sebastien Deleuze
* @since 7.0
*/
@@ -62,7 +59,7 @@ public class JacksonJsonMessageConverter implements SmartMessageConverter, BeanC
public static final String DEFAULT_ENCODING = "UTF-8";
private final JsonMapper jsonMapper;
private final JsonMapper mapper;
private MessageType targetType = MessageType.BYTES;
@@ -85,17 +82,27 @@ public class JacksonJsonMessageConverter implements SmartMessageConverter, BeanC
* {@link MapperBuilder#findModules(ClassLoader)}.
*/
public JacksonJsonMessageConverter() {
this.jsonMapper = JsonMapper.builder().findAndAddModules(JacksonJsonMessageConverter.class.getClassLoader()).build();
this(JsonMapper.builder());
}
/**
* Construct a new instance with the provided {@link JsonMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s found
* by {@link MapperBuilder#findModules(ClassLoader)}.
* @see JsonMapper#builder()
*/
public JacksonJsonMessageConverter(JsonMapper.Builder builder) {
Assert.notNull(builder, "JsonMapper.Builder must not be null");
this.mapper = builder.findAndAddModules(JacksonJsonMessageConverter.class.getClassLoader()).build();
}
/**
* Construct a new instance with the provided {@link JsonMapper}.
* @see JsonMapper#builder()
* @see MapperBuilder#findModules(ClassLoader)
*/
public JacksonJsonMessageConverter(JsonMapper jsonMapper) {
Assert.notNull(jsonMapper, "JsonMapper must not be null");
this.jsonMapper = jsonMapper;
public JacksonJsonMessageConverter(JsonMapper mapper) {
Assert.notNull(mapper, "JsonMapper must not be null");
this.mapper = mapper;
}
/**
@@ -172,9 +179,9 @@ public class JacksonJsonMessageConverter implements SmartMessageConverter, BeanC
Message message;
try {
message = switch (this.targetType) {
case TEXT -> mapToTextMessage(object, session, this.jsonMapper.writer());
case BYTES -> mapToBytesMessage(object, session, this.jsonMapper.writer());
default -> mapToMessage(object, session, this.jsonMapper.writer(), this.targetType);
case TEXT -> mapToTextMessage(object, session, this.mapper.writer());
case BYTES -> mapToBytesMessage(object, session, this.mapper.writer());
default -> mapToMessage(object, session, this.mapper.writer(), this.targetType);
};
}
catch (IOException ex) {
@@ -205,10 +212,10 @@ public class JacksonJsonMessageConverter implements SmartMessageConverter, BeanC
throws JMSException, MessageConversionException {
if (jsonView != null) {
return toMessage(object, session, this.jsonMapper.writerWithView(jsonView));
return toMessage(object, session, this.mapper.writerWithView(jsonView));
}
else {
return toMessage(object, session, this.jsonMapper.writer());
return toMessage(object, session, this.mapper.writer());
}
}
@@ -362,7 +369,7 @@ public class JacksonJsonMessageConverter implements SmartMessageConverter, BeanC
throws JMSException, IOException {
String body = message.getText();
return this.jsonMapper.readValue(body, targetJavaType);
return this.mapper.readValue(body, targetJavaType);
}
/**
@@ -385,7 +392,7 @@ public class JacksonJsonMessageConverter implements SmartMessageConverter, BeanC
if (encoding != null) {
try {
String body = new String(bytes, encoding);
return this.jsonMapper.readValue(body, targetJavaType);
return this.mapper.readValue(body, targetJavaType);
}
catch (UnsupportedEncodingException ex) {
throw new MessageConversionException("Cannot convert bytes to String", ex);
@@ -393,7 +400,7 @@ public class JacksonJsonMessageConverter implements SmartMessageConverter, BeanC
}
else {
// Jackson internally performs encoding detection, falling back to UTF-8.
return this.jsonMapper.readValue(bytes, targetJavaType);
return this.mapper.readValue(bytes, targetJavaType);
}
}
@@ -436,11 +443,11 @@ public class JacksonJsonMessageConverter implements SmartMessageConverter, BeanC
}
Class<?> mappedClass = this.idClassMappings.get(typeId);
if (mappedClass != null) {
return this.jsonMapper.constructType(mappedClass);
return this.mapper.constructType(mappedClass);
}
try {
Class<?> typeClass = ClassUtils.forName(typeId, this.beanClassLoader);
return this.jsonMapper.constructType(typeClass);
return this.mapper.constructType(typeClass);
}
catch (Throwable ex) {
throw new MessageConversionException("Failed to resolve type id [" + typeId + "]", ex);
@@ -40,9 +40,6 @@ import org.springframework.util.MimeType;
/**
* A Jackson 3.x based {@link MessageConverter} implementation.
*
* <p>The default constructor loads {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
*
* @author Sebastien Deleuze
* @since 7.0
*/
@@ -51,7 +48,7 @@ public class JacksonJsonMessageConverter extends AbstractMessageConverter {
private static final MimeType[] DEFAULT_MIME_TYPES = new MimeType[] {
new MimeType("application", "json"), new MimeType("application", "*+json")};
private final JsonMapper jsonMapper;
private final JsonMapper mapper;
/**
@@ -71,36 +68,58 @@ public class JacksonJsonMessageConverter extends AbstractMessageConverter {
* @param supportedMimeTypes the supported MIME types
*/
public JacksonJsonMessageConverter(MimeType... supportedMimeTypes) {
super(supportedMimeTypes);
this.jsonMapper = JsonMapper.builder().findAndAddModules(JacksonJsonMessageConverter.class.getClassLoader()).build();
this(JsonMapper.builder(), supportedMimeTypes);
}
/**
* Construct a new instance with the provided {@link JsonMapper}.
* @see JsonMapper#builder()
* @see MapperBuilder#findModules(ClassLoader)
*/
public JacksonJsonMessageConverter(JsonMapper jsonMapper) {
this(jsonMapper, DEFAULT_MIME_TYPES);
public JacksonJsonMessageConverter(JsonMapper mapper) {
this(mapper, DEFAULT_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link JsonMapper.Builder} customized
* with the {@link tools.jackson.databind.JacksonModule}s found
* by {@link MapperBuilder#findModules(ClassLoader)}.
* @see JsonMapper#builder()
*/
public JacksonJsonMessageConverter(JsonMapper.Builder builder) {
this(builder, DEFAULT_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link JsonMapper} and the
* provided {@link MimeType}s.
* @see JsonMapper#builder()
* @see MapperBuilder#findModules(ClassLoader)
*/
public JacksonJsonMessageConverter(JsonMapper jsonMapper, MimeType... supportedMimeTypes) {
public JacksonJsonMessageConverter(JsonMapper mapper, MimeType... supportedMimeTypes) {
super(supportedMimeTypes);
Assert.notNull(jsonMapper, "JsonMapper must not be null");
this.jsonMapper = jsonMapper;
Assert.notNull(mapper, "JsonMapper must not be null");
this.mapper = mapper;
}
/**
* Construct a new instance with the provided {@link JsonMapper} customized
* with the {@link tools.jackson.databind.JacksonModule}s found by
* {@link MapperBuilder#findModules(ClassLoader)}, and the provided
* {@link MimeType}s.
* @see JsonMapper#builder()
*/
public JacksonJsonMessageConverter(JsonMapper.Builder builder, MimeType... supportedMimeTypes) {
super(supportedMimeTypes);
Assert.notNull(builder, "JsonMapper.Builder must not be null");
this.mapper = builder.findAndAddModules(JacksonJsonMessageConverter.class.getClassLoader()).build();
}
/**
* Return the underlying {@code JsonMapper} for this converter.
*/
protected JsonMapper getJsonMapper() {
return this.jsonMapper;
return this.mapper;
}
@Override
@@ -121,7 +140,7 @@ public class JacksonJsonMessageConverter extends AbstractMessageConverter {
@Override
protected @Nullable Object convertFromInternal(Message<?> message, Class<?> targetClass, @Nullable Object conversionHint) {
JavaType javaType = this.jsonMapper.constructType(getResolvedType(targetClass, conversionHint));
JavaType javaType = this.mapper.constructType(getResolvedType(targetClass, conversionHint));
Object payload = message.getPayload();
Class<?> view = getSerializationView(conversionHint);
try {
@@ -130,19 +149,19 @@ public class JacksonJsonMessageConverter extends AbstractMessageConverter {
}
else if (payload instanceof byte[] bytes) {
if (view != null) {
return this.jsonMapper.readerWithView(view).forType(javaType).readValue(bytes);
return this.mapper.readerWithView(view).forType(javaType).readValue(bytes);
}
else {
return this.jsonMapper.readValue(bytes, javaType);
return this.mapper.readValue(bytes, javaType);
}
}
else {
// Assuming a text-based source payload
if (view != null) {
return this.jsonMapper.readerWithView(view).forType(javaType).readValue(payload.toString());
return this.mapper.readerWithView(view).forType(javaType).readValue(payload.toString());
}
else {
return this.jsonMapper.readValue(payload.toString(), javaType);
return this.mapper.readValue(payload.toString(), javaType);
}
}
}
@@ -160,12 +179,12 @@ public class JacksonJsonMessageConverter extends AbstractMessageConverter {
if (byte[].class == getSerializedPayloadClass()) {
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
JsonEncoding encoding = getJsonEncoding(getMimeType(headers));
try (JsonGenerator generator = this.jsonMapper.createGenerator(out, encoding)) {
try (JsonGenerator generator = this.mapper.createGenerator(out, encoding)) {
if (view != null) {
this.jsonMapper.writerWithView(view).writeValue(generator, payload);
this.mapper.writerWithView(view).writeValue(generator, payload);
}
else {
this.jsonMapper.writeValue(generator, payload);
this.mapper.writeValue(generator, payload);
}
payload = out.toByteArray();
}
@@ -174,10 +193,10 @@ public class JacksonJsonMessageConverter extends AbstractMessageConverter {
// Assuming a text-based target payload
Writer writer = new StringWriter(1024);
if (view != null) {
this.jsonMapper.writerWithView(view).writeValue(writer, payload);
this.mapper.writerWithView(view).writeValue(writer, payload);
}
else {
this.jsonMapper.writeValue(writer, payload);
this.mapper.writeValue(writer, payload);
}
payload = writer.toString();
}
@@ -51,17 +51,38 @@ public class JacksonCborDecoder extends AbstractJacksonDecoder<CBORMapper> {
super(CBORMapper.builder(), MediaType.APPLICATION_CBOR);
}
/**
* Construct a new instance with the provided {@link CBORMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
* @see CBORMapper#builder()
*/
public JacksonCborDecoder(CBORMapper.Builder builder) {
super(builder, MediaType.APPLICATION_CBOR);
}
/**
* Construct a new instance with the provided {@link CBORMapper}.
* @see CBORMapper#builder()
*/
public JacksonCborDecoder(CBORMapper mapper) {
super(mapper, MediaType.APPLICATION_CBOR);
}
/**
* Construct a new instance with the provided {@link CBORMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}, and
* {@link MimeType}s.
* @see CBORMapper#builder()
*/
public JacksonCborDecoder(CBORMapper.Builder builder, MimeType... mimeTypes) {
super(builder, mimeTypes);
}
/**
* Construct a new instance with the provided {@link CBORMapper} and {@link MimeType}s.
* @see CBORMapper#builder()
* @see MapperBuilder#findAndAddModules(ClassLoader)
*/
public JacksonCborDecoder(CBORMapper mapper, MimeType... mimeTypes) {
super(mapper, mimeTypes);
@@ -52,19 +52,38 @@ public class JacksonCborEncoder extends AbstractJacksonEncoder<CBORMapper> {
super(CBORMapper.builder(), MediaType.APPLICATION_CBOR);
}
/**
* Construct a new instance with the provided {@link CBORMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
* @see CBORMapper#builder()
*/
public JacksonCborEncoder(CBORMapper.Builder builder) {
super(builder, MediaType.APPLICATION_CBOR);
}
/**
* Construct a new instance with the provided {@link CBORMapper}.
* @see CBORMapper#builder()
* @see MapperBuilder#findAndAddModules(ClassLoader)
*/
public JacksonCborEncoder(CBORMapper mapper) {
super(mapper, MediaType.APPLICATION_CBOR);
}
/**
* Construct a new instance with the provided {@link CBORMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}, and
* {@link MimeType}s.
* @see CBORMapper#builder()
*/
public JacksonCborEncoder(CBORMapper.Builder builder, MimeType... mimeTypes) {
super(builder, mimeTypes);
}
/**
* Construct a new instance with the provided {@link CBORMapper} and {@link MimeType}s.
* @see CBORMapper#builder()
* @see MapperBuilder#findAndAddModules(ClassLoader)
*/
public JacksonCborEncoder(CBORMapper mapper, MimeType... mimeTypes) {
super(mapper, mimeTypes);
@@ -42,9 +42,6 @@ import org.springframework.util.MimeTypeUtils;
* <a href="https://github.com/FasterXML/jackson">Jackson 3.x</a>
* leveraging non-blocking parsing.
*
* <p>The default constructor loads {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
*
* @author Sebastien Deleuze
* @since 7.0
* @see JacksonJsonEncoder
@@ -71,19 +68,37 @@ public class JacksonJsonDecoder extends AbstractJacksonDecoder<JsonMapper> {
super(JsonMapper.builder(), DEFAULT_JSON_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link JsonMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
* @see JsonMapper#builder()
*/
public JacksonJsonDecoder(JsonMapper.Builder builder) {
super(builder, DEFAULT_JSON_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link JsonMapper}.
* @see JsonMapper#builder()
* @see MapperBuilder#findModules(ClassLoader)
*/
public JacksonJsonDecoder(JsonMapper mapper) {
this(mapper, DEFAULT_JSON_MIME_TYPES);
super(mapper, DEFAULT_JSON_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link JsonMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}, and
* {@link MimeType}s.
* @see JsonMapper#builder()
*/
public JacksonJsonDecoder(JsonMapper.Builder builder, MimeType... mimeTypes) {
super(builder, mimeTypes);
}
/**
* Construct a new instance with the provided {@link JsonMapper} and {@link MimeType}s.
* @see JsonMapper#builder()
* @see MapperBuilder#findModules(ClassLoader)
*/
public JacksonJsonDecoder(JsonMapper mapper, MimeType... mimeTypes) {
super(mapper, mimeTypes);
@@ -43,9 +43,6 @@ import org.springframework.util.MimeType;
* use cases, {@link Flux} elements are collected into a {@link List} before
* serialization for performance reason.
*
* <p>The default constructor loads {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
*
* @author Sebastien Deleuze
* @since 7.0
* @see JacksonJsonDecoder
@@ -72,26 +69,44 @@ public class JacksonJsonEncoder extends AbstractJacksonEncoder<JsonMapper> {
* {@link ProblemDetailJacksonMixin}.
*/
public JacksonJsonEncoder() {
super(JsonMapper.builder().addMixIn(ProblemDetail.class, ProblemDetailJacksonMixin.class),
DEFAULT_JSON_MIME_TYPES);
setStreamingMediaTypes(List.of(MediaType.APPLICATION_NDJSON));
this.ssePrettyPrinter = initSsePrettyPrinter();
this(JsonMapper.builder(), DEFAULT_JSON_MIME_TYPES);
}
/**
* Construct a new instance with a {@link JsonMapper.Builder} customized
* with the {@link tools.jackson.databind.JacksonModule}s found by
* {@link MapperBuilder#findModules(ClassLoader)} and
* {@link ProblemDetailJacksonMixin}.
* @see JsonMapper#builder()
*/
public JacksonJsonEncoder(JsonMapper.Builder builder) {
this(builder, DEFAULT_JSON_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link JsonMapper}.
* @see JsonMapper#builder()
* @see MapperBuilder#findModules(ClassLoader)
*/
public JacksonJsonEncoder(JsonMapper mapper) {
this(mapper, DEFAULT_JSON_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link JsonMapper.Builder} customized
* with the {@link tools.jackson.databind.JacksonModule}s found by
* {@link MapperBuilder#findModules(ClassLoader)} and
* {@link ProblemDetailJacksonMixin}, and {@link MimeType}s.
* @see JsonMapper#builder()
*/
public JacksonJsonEncoder(JsonMapper.Builder builder, MimeType... mimeTypes) {
super(builder.addMixIn(ProblemDetail.class, ProblemDetailJacksonMixin.class), mimeTypes);
setStreamingMediaTypes(List.of(MediaType.APPLICATION_NDJSON));
this.ssePrettyPrinter = initSsePrettyPrinter();
}
/**
* Construct a new instance with the provided {@link JsonMapper} and
* {@link MimeType}s.
* @see JsonMapper#builder()
* @see MapperBuilder#findModules(ClassLoader)
*/
public JacksonJsonEncoder(JsonMapper mapper, MimeType... mimeTypes) {
super(mapper, mimeTypes);
@@ -26,9 +26,6 @@ import org.springframework.util.MimeType;
* Decode a byte stream into Smile and convert to Objects with Jackson 3.x,
* leveraging non-blocking parsing.
*
* <p>The default constructor loads {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
*
* @author Sebastien Deleuze
* @since 7.0
* @see JacksonSmileEncoder
@@ -44,24 +41,44 @@ public class JacksonSmileDecoder extends AbstractJacksonDecoder<SmileMapper> {
* Construct a new instance with a {@link SmileMapper} customized with the
* {@link tools.jackson.databind.JacksonModule}s found by
* {@link MapperBuilder#findModules(ClassLoader)}.
* @see SmileMapper#builder()
*/
public JacksonSmileDecoder() {
super(SmileMapper.builder(), DEFAULT_SMILE_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link SmileMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
* @see SmileMapper#builder()
*/
public JacksonSmileDecoder(SmileMapper.Builder builder) {
this(builder, DEFAULT_SMILE_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link SmileMapper}.
* @see SmileMapper#builder()
* @see MapperBuilder#findAndAddModules(ClassLoader)
*/
public JacksonSmileDecoder(SmileMapper mapper) {
this(mapper, DEFAULT_SMILE_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link SmileMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}, and
* {@link MimeType}s.
* @see SmileMapper#builder()
*/
public JacksonSmileDecoder(SmileMapper.Builder builder, MimeType... mimeTypes) {
super(builder, mimeTypes);
}
/**
* Construct a new instance with the provided {@link SmileMapper} and {@link MimeType}s.
* @see SmileMapper#builder()
* @see MapperBuilder#findAndAddModules(ClassLoader)
*/
public JacksonSmileDecoder(SmileMapper mapper, MimeType... mimeTypes) {
super(mapper, mimeTypes);
@@ -34,9 +34,6 @@ import org.springframework.util.MimeType;
* <p>For non-streaming use cases, {@link Flux} elements are collected into a {@link List}
* before serialization for performance reasons.
*
* <p>The default constructor loads {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
*
* @author Sebastien Deleuze
* @since 7.0
* @see JacksonSmileDecoder
@@ -59,24 +56,42 @@ public class JacksonSmileEncoder extends AbstractJacksonEncoder<SmileMapper> {
* {@link MapperBuilder#findModules(ClassLoader)}.
*/
public JacksonSmileEncoder() {
super(SmileMapper.builder(), DEFAULT_SMILE_MIME_TYPES);
setStreamingMediaTypes(Collections.singletonList(DEFAULT_SMILE_STREAMING_MEDIA_TYPE));
this(SmileMapper.builder(), DEFAULT_SMILE_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link SmileMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
* @see SmileMapper#builder()
*/
public JacksonSmileEncoder(SmileMapper.Builder builder) {
this(builder, DEFAULT_SMILE_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link SmileMapper}.
* @see SmileMapper#builder()
* @see MapperBuilder#findAndAddModules(ClassLoader)
*/
public JacksonSmileEncoder(SmileMapper mapper) {
super(mapper, DEFAULT_SMILE_MIME_TYPES);
this(mapper, DEFAULT_SMILE_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link SmileMapper}
* customized with the {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}, and
* {@link MimeType}s.
* @see SmileMapper#builder()
*/
public JacksonSmileEncoder(SmileMapper.Builder builder, MimeType... mimeTypes) {
super(builder, mimeTypes);
setStreamingMediaTypes(Collections.singletonList(DEFAULT_SMILE_STREAMING_MEDIA_TYPE));
}
/**
* Construct a new instance with the provided {@link SmileMapper} and {@link MimeType}s.
* @see SmileMapper#builder()
* @see MapperBuilder#findAndAddModules(ClassLoader)
*/
public JacksonSmileEncoder(SmileMapper mapper, MimeType... mimeTypes) {
super(mapper, mimeTypes);
@@ -32,8 +32,13 @@ import org.springframework.http.converter.AbstractJacksonHttpMessageConverter;
* media type. This can be overridden by setting the {@link #setSupportedMediaTypes
* supportedMediaTypes} property.
*
* <p>The default constructor loads {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
* <p>The following hints entries are supported:
* <ul>
* <li>A JSON view with a <code>"com.fasterxml.jackson.annotation.JsonView"</code>
* key and the class name of the JSON view as value.</li>
* <li>A filter provider with a <code>"tools.jackson.databind.ser.FilterProvider"</code>
* key and the filter provider class name as value.</li>
* </ul>
*
* @author Sebastien Deleuze
* @since 7.0
@@ -49,10 +54,19 @@ public class JacksonCborHttpMessageConverter extends AbstractJacksonHttpMessageC
super(CBORMapper.builder(), MediaType.APPLICATION_CBOR);
}
/**
* Construct a new instance with the provided {@link CBORMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
* @see CBORMapper#builder()
*/
public JacksonCborHttpMessageConverter(CBORMapper.Builder builder) {
super(builder, MediaType.APPLICATION_CBOR);
}
/**
* Construct a new instance with the provided {@link CBORMapper}.
* @see CBORMapper#builder()
* @see MapperBuilder#findAndAddModules(ClassLoader)
*/
public JacksonCborHttpMessageConverter(CBORMapper mapper) {
super(mapper, MediaType.APPLICATION_CBOR);
@@ -41,14 +41,11 @@ import org.springframework.http.converter.AbstractJacksonHttpMessageConverter;
* can be overridden by setting the {@link #setSupportedMediaTypes supportedMediaTypes}
* property.
*
* <p>The default constructor loads {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
*
* <p>The following hints entries are supported:
* <ul>
* <li>A JSON view with a <code>com.fasterxml.jackson.annotation.JsonView</code>
* <li>A JSON view with a <code>"com.fasterxml.jackson.annotation.JsonView"</code>
* key and the class name of the JSON view as value.</li>
* <li>A filter provider with a <code>tools.jackson.databind.ser.FilterProvider</code>
* <li>A filter provider with a <code>"tools.jackson.databind.ser.FilterProvider"</code>
* key and the filter provider class name as value.</li>
* </ul>
*
@@ -74,16 +71,26 @@ public class JacksonJsonHttpMessageConverter extends AbstractJacksonHttpMessageC
* {@link ProblemDetailJacksonMixin}.
*/
public JacksonJsonHttpMessageConverter() {
super(JsonMapper.builder().addMixIn(ProblemDetail.class, ProblemDetailJacksonMixin.class), DEFAULT_JSON_MIME_TYPES);
this(JsonMapper.builder());
}
/**
* Construct a new instance with the provided {@link JsonMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s found
* by {@link MapperBuilder#findModules(ClassLoader)} and
* {@link ProblemDetailJacksonMixin}.
* @see JsonMapper#builder()
*/
public JacksonJsonHttpMessageConverter(JsonMapper.Builder builder) {
super(builder.addMixIn(ProblemDetail.class, ProblemDetailJacksonMixin.class), DEFAULT_JSON_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link JsonMapper}.
* @see JsonMapper#builder()
* @see MapperBuilder#findModules(ClassLoader)
*/
public JacksonJsonHttpMessageConverter(JsonMapper objectMapper) {
super(objectMapper, DEFAULT_JSON_MIME_TYPES);
public JacksonJsonHttpMessageConverter(JsonMapper mapper) {
super(mapper, DEFAULT_JSON_MIME_TYPES);
}
@@ -32,8 +32,13 @@ import org.springframework.http.converter.AbstractJacksonHttpMessageConverter;
* media type. This can be overridden by setting the
* {@link #setSupportedMediaTypes supportedMediaTypes} property.
*
* <p>The default constructor loads {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
* <p>The following hints entries are supported:
* <ul>
* <li>A JSON view with a <code>"com.fasterxml.jackson.annotation.JsonView"</code>
* key and the class name of the JSON view as value.</li>
* <li>A filter provider with a <code>"tools.jackson.databind.ser.FilterProvider"</code>
* key and the filter provider class name as value.</li>
* </ul>
*
* @author Sebastien Deleuze
* @since 7.0
@@ -51,10 +56,19 @@ public class JacksonSmileHttpMessageConverter extends AbstractJacksonHttpMessage
super(SmileMapper.builder(), DEFAULT_SMILE_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link SmileMapper} customized
* with the {@link tools.jackson.databind.JacksonModule}s found by
* {@link MapperBuilder#findModules(ClassLoader)}.
* @see SmileMapper#builder()
*/
public JacksonSmileHttpMessageConverter(SmileMapper.Builder builder) {
super(builder, DEFAULT_SMILE_MIME_TYPES);
}
/**
* Construct a new instance with the provided {@link SmileMapper}.
* @see SmileMapper#builder()
* @see MapperBuilder#findAndAddModules(ClassLoader)
*/
public JacksonSmileHttpMessageConverter(SmileMapper mapper) {
super(mapper, DEFAULT_SMILE_MIME_TYPES);
@@ -39,9 +39,6 @@ import org.springframework.util.xml.StaxUtils;
* {@code application/*+xml} with {@code UTF-8} character set. This can be overridden by
* setting the {@link #setSupportedMediaTypes supportedMediaTypes} property.
*
* <p>The default constructor loads {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
*
* <p>The following hint entries are supported:
* <ul>
* <li>A JSON view with a <code>com.fasterxml.jackson.annotation.JsonView</code>
@@ -80,6 +77,7 @@ public class JacksonXmlHttpMessageConverter extends AbstractJacksonHttpMessageCo
* customized with the {@link tools.jackson.databind.JacksonModule}s found by
* {@link MapperBuilder#findModules(ClassLoader)} and
* {@link ProblemDetailJacksonXmlMixin}.
* @see XmlMapper#builder()
*/
public JacksonXmlHttpMessageConverter(XmlMapper.Builder builder) {
super(builder.addMixIn(ProblemDetail.class, ProblemDetailJacksonXmlMixin.class), DEFAULT_XML_MIME_TYPES);
@@ -88,7 +86,6 @@ public class JacksonXmlHttpMessageConverter extends AbstractJacksonHttpMessageCo
/**
* Construct a new instance with the provided {@link XmlMapper}.
* @see XmlMapper#builder()
* @see MapperBuilder#findModules(ClassLoader)
*/
public JacksonXmlHttpMessageConverter(XmlMapper xmlMapper) {
super(xmlMapper, DEFAULT_XML_MIME_TYPES);
@@ -32,8 +32,13 @@ import org.springframework.http.converter.AbstractJacksonHttpMessageConverter;
* media type. This can be overridden by setting the {@link #setSupportedMediaTypes
* supportedMediaTypes} property.
*
* <p>The default constructor loads {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
* <p>The following hints entries are supported:
* <ul>
* <li>A JSON view with a <code>"com.fasterxml.jackson.annotation.JsonView"</code>
* key and the class name of the JSON view as value.</li>
* <li>A filter provider with a <code>"tools.jackson.databind.ser.FilterProvider"</code>
* key and the filter provider class name as value.</li>
* </ul>
*
* @author Sebastien Deleuze
* @since 7.0
@@ -52,7 +57,16 @@ public class JacksonYamlHttpMessageConverter extends AbstractJacksonHttpMessageC
/**
* Construct a new instance with the provided {@link YAMLMapper}.
* @see YAMLMapper#builder()
* @see MapperBuilder#findAndAddModules(ClassLoader)
*/
public JacksonYamlHttpMessageConverter(YAMLMapper.Builder builder) {
super(builder, MediaType.APPLICATION_YAML);
}
/**
* Construct a new instance with the provided {@link YAMLMapper.Builder} customized
* with the {@link tools.jackson.databind.JacksonModule}s found by
* {@link MapperBuilder#findModules(ClassLoader)}.
* @see YAMLMapper#builder()
*/
public JacksonYamlHttpMessageConverter(YAMLMapper mapper) {
super(mapper, MediaType.APPLICATION_YAML);
@@ -65,7 +65,7 @@ public abstract class AbstractJacksonView extends AbstractView {
private static volatile @Nullable List<JacksonModule> modules;
private final ObjectMapper objectMapper;
private final ObjectMapper mapper;
private JsonEncoding encoding = JsonEncoding.UTF8;
@@ -75,13 +75,13 @@ public abstract class AbstractJacksonView extends AbstractView {
protected AbstractJacksonView(MapperBuilder<?, ?> builder, String contentType) {
this.objectMapper = builder.addModules(initModules()).build();
this.mapper = builder.addModules(initModules()).build();
setContentType(contentType);
setExposePathVariables(false);
}
protected AbstractJacksonView(ObjectMapper objectMapper, String contentType) {
this.objectMapper = objectMapper;
protected AbstractJacksonView(ObjectMapper mapper, String contentType) {
this.mapper = mapper;
setContentType(contentType);
setExposePathVariables(false);
}
@@ -185,7 +185,7 @@ public abstract class AbstractJacksonView extends AbstractView {
* @throws IOException if writing failed
*/
protected void writeContent(OutputStream stream, Object object, @Nullable Map<String, Object> hints) throws IOException {
try (JsonGenerator generator = this.objectMapper.createGenerator(stream, this.encoding)) {
try (JsonGenerator generator = this.mapper.createGenerator(stream, this.encoding)) {
writePrefix(generator, object);
Class<?> serializationView = null;
@@ -196,7 +196,7 @@ public abstract class AbstractJacksonView extends AbstractView {
}
ObjectWriter objectWriter = (serializationView != null ?
this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer());
this.mapper.writerWithView(serializationView) : this.mapper.writer());
if (filters != null) {
objectWriter = objectWriter.with(filters);
}
@@ -77,12 +77,24 @@ public class JacksonJsonView extends AbstractJacksonView {
super(JsonMapper.builder(), DEFAULT_CONTENT_TYPE);
}
/**
* Construct a new instance using the provided {@link JsonMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)} and setting
* the content type to {@code application/json}.
* @see JsonMapper#builder()
*/
public JacksonJsonView(JsonMapper.Builder builder) {
super(builder, DEFAULT_CONTENT_TYPE);
}
/**
* Construct a new instance using the provided {@link JsonMapper}
* and setting the content type to {@value #DEFAULT_CONTENT_TYPE}.
* @see JsonMapper#builder()
*/
public JacksonJsonView(JsonMapper jsonMapper) {
super(jsonMapper, DEFAULT_CONTENT_TYPE);
public JacksonJsonView(JsonMapper mapper) {
super(mapper, DEFAULT_CONTENT_TYPE);
}
@@ -70,12 +70,24 @@ public class JacksonXmlView extends AbstractJacksonView {
super(XmlMapper.builder(), DEFAULT_CONTENT_TYPE);
}
/**
* Construct a new instance using the provided {@link XmlMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)} and setting
* the content type to {@code application/xml}.
* @see XmlMapper#builder()
*/
public JacksonXmlView(XmlMapper.Builder builder) {
super(builder, DEFAULT_CONTENT_TYPE);
}
/**
* Construct a new instance using the provided {@link XmlMapper}
* and setting the content type to {@code application/xml}.
* @see XmlMapper#builder()
*/
public JacksonXmlView(XmlMapper xmlMapper) {
super(xmlMapper, DEFAULT_CONTENT_TYPE);
public JacksonXmlView(XmlMapper mapper) {
super(mapper, DEFAULT_CONTENT_TYPE);
}
@@ -28,45 +28,53 @@ import org.springframework.util.Assert;
/**
* A Jackson 3.x codec for encoding and decoding SockJS messages.
*
* <p>The default constructor loads {@link tools.jackson.databind.JacksonModule}s
* found by {@link MapperBuilder#findModules(ClassLoader)}.
*
* @author Sebastien Deleuze
* @since 7.0
*/
public class JacksonJsonSockJsMessageCodec extends AbstractSockJsMessageCodec {
private final JsonMapper jsonMapper;
private final JsonMapper mapper;
/**
* Construct a new instance with a {@link JsonMapper} customized with the
* {@link tools.jackson.databind.JacksonModule}s found by
* {@link MapperBuilder#findModules(ClassLoader)}.
* @see JsonMapper#builder()
*/
public JacksonJsonSockJsMessageCodec() {
this.jsonMapper = JsonMapper.builder().findAndAddModules(JacksonJsonSockJsMessageCodec.class.getClassLoader()).build();
this(JsonMapper.builder());
}
/**
* Construct a new instance with the provided {@link JsonMapper.Builder}
* customized with the {@link tools.jackson.databind.JacksonModule}s found by
* {@link MapperBuilder#findModules(ClassLoader)}.
* @see JsonMapper#builder()
*/
public JacksonJsonSockJsMessageCodec(JsonMapper.Builder builder) {
Assert.notNull(builder, "JsonMapper.Builder must not be null");
this.mapper = builder.findAndAddModules(JacksonJsonSockJsMessageCodec.class.getClassLoader()).build();
}
/**
* Construct a new instance with the provided {@link JsonMapper}.
* @see JsonMapper#builder()
* @see MapperBuilder#findAndAddModules(ClassLoader)
*/
public JacksonJsonSockJsMessageCodec(JsonMapper jsonMapper) {
Assert.notNull(jsonMapper, "JsonMapper must not be null");
this.jsonMapper = jsonMapper;
public JacksonJsonSockJsMessageCodec(JsonMapper mapper) {
Assert.notNull(mapper, "JsonMapper must not be null");
this.mapper = mapper;
}
@Override
public String @Nullable [] decode(String content) {
return this.jsonMapper.readValue(content, String[].class);
return this.mapper.readValue(content, String[].class);
}
@Override
public String @Nullable [] decodeInputStream(InputStream content) {
return this.jsonMapper.readValue(content, String[].class);
return this.mapper.readValue(content, String[].class);
}
@Override