Provide Gson/JSON-B MessageConverter for spring-messaging (aligned with spring-web)
Closes gh-21496
This commit is contained in:
parent
ad5072a43c
commit
214bc407b4
|
@ -12,6 +12,8 @@ dependencies {
|
|||
optional("io.rsocket:rsocket-core")
|
||||
optional("io.rsocket:rsocket-transport-netty")
|
||||
optional("com.fasterxml.jackson.core:jackson-databind")
|
||||
optional("com.google.code.gson:gson")
|
||||
optional("javax.json.bind:javax.json.bind-api")
|
||||
optional("javax.xml.bind:jaxb-api")
|
||||
optional("com.google.protobuf:protobuf-java-util")
|
||||
optional("org.jetbrains.kotlinx:kotlinx-coroutines-core")
|
||||
|
@ -31,8 +33,10 @@ dependencies {
|
|||
testCompile("org.jetbrains.kotlin:kotlin-stdlib")
|
||||
testCompile("org.xmlunit:xmlunit-assertj")
|
||||
testCompile("org.xmlunit:xmlunit-matchers")
|
||||
testRuntime("com.sun.activation:javax.activation")
|
||||
testRuntime("com.sun.xml.bind:jaxb-core")
|
||||
testRuntime("com.sun.xml.bind:jaxb-impl")
|
||||
testRuntime("com.sun.activation:javax.activation")
|
||||
testRuntime("javax.json:javax.json-api")
|
||||
testRuntime("org.apache.johnzon:johnzon-jsonb")
|
||||
testRuntime(project(":spring-context"))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.messaging.converter;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
/**
|
||||
* Common base class for plain JSON converters, e.g. Gson and JSON-B.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.3
|
||||
* @see GsonMessageConverter
|
||||
* @see JsonbMessageConverter
|
||||
* @see #fromJson(Reader, Type)
|
||||
* @see #fromJson(String, Type)
|
||||
* @see #toJson(Object, Type)
|
||||
* @see #toJson(Object, Type, Writer)
|
||||
*/
|
||||
public abstract class AbstractJsonMessageConverter extends AbstractMessageConverter {
|
||||
|
||||
private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
|
||||
protected AbstractJsonMessageConverter() {
|
||||
super(new MimeType("application", "json"));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object convertFromInternal(Message<?> message, Class<?> targetClass, @Nullable Object conversionHint) {
|
||||
try {
|
||||
Type resolvedType = getResolvedType(targetClass, conversionHint);
|
||||
Object payload = message.getPayload();
|
||||
if (ClassUtils.isAssignableValue(targetClass, payload)) {
|
||||
return payload;
|
||||
}
|
||||
else if (payload instanceof byte[]) {
|
||||
return fromJson(getReader((byte[]) payload, message.getHeaders()), resolvedType);
|
||||
}
|
||||
else {
|
||||
// Assuming a text-based source payload
|
||||
return fromJson(payload.toString(), resolvedType);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new MessageConversionException(message, "Could not read JSON: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object convertToInternal(Object payload, @Nullable MessageHeaders headers, @Nullable Object conversionHint) {
|
||||
try {
|
||||
Type resolvedType = getResolvedType(payload.getClass(), conversionHint);
|
||||
if (byte[].class == getSerializedPayloadClass()) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
|
||||
Writer writer = getWriter(out, headers);
|
||||
toJson(payload, resolvedType, writer);
|
||||
writer.flush();
|
||||
return out.toByteArray();
|
||||
}
|
||||
else {
|
||||
// Assuming a text-based target payload
|
||||
return toJson(payload, resolvedType);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new MessageConversionException("Could not write JSON: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Reader getReader(byte[] payload, @Nullable MessageHeaders headers) {
|
||||
InputStream in = new ByteArrayInputStream(payload);
|
||||
return new InputStreamReader(in, getCharsetToUse(headers));
|
||||
}
|
||||
|
||||
private Writer getWriter(ByteArrayOutputStream out, @Nullable MessageHeaders headers) {
|
||||
return new OutputStreamWriter(out, getCharsetToUse(headers));
|
||||
}
|
||||
|
||||
private Charset getCharsetToUse(@Nullable MessageHeaders headers) {
|
||||
MimeType mimeType = getMimeType(headers);
|
||||
return (mimeType != null && mimeType.getCharset() != null ? mimeType.getCharset() : DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
|
||||
protected abstract Object fromJson(Reader reader, Type resolvedType);
|
||||
|
||||
protected abstract Object fromJson(String payload, Type resolvedType);
|
||||
|
||||
protected abstract void toJson(Object payload, Type resolvedType, Writer writer);
|
||||
|
||||
protected abstract String toJson(Object payload, Type resolvedType);
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.messaging.converter;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -25,6 +26,8 @@ import java.util.List;
|
|||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
|
@ -92,7 +95,7 @@ public abstract class AbstractMessageConverter implements SmartMessageConverter
|
|||
}
|
||||
|
||||
/**
|
||||
* Allows sub-classes to add more supported mime types.
|
||||
* Allows subclasses to add more supported mime types.
|
||||
* @since 5.2.2
|
||||
*/
|
||||
protected void addSupportedMimeTypes(MimeType... supportedMimeTypes) {
|
||||
|
@ -167,21 +170,6 @@ public abstract class AbstractMessageConverter implements SmartMessageConverter
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the default content type for the payload. Called when
|
||||
* {@link #toMessage(Object, MessageHeaders)} is invoked without message headers or
|
||||
* without a content type header.
|
||||
* <p>By default, this returns the first element of the {@link #getSupportedMimeTypes()
|
||||
* supportedMimeTypes}, if any. Can be overridden in sub-classes.
|
||||
* @param payload the payload being converted to message
|
||||
* @return the content type, or {@code null} if not known
|
||||
*/
|
||||
@Nullable
|
||||
protected MimeType getDefaultContentType(Object payload) {
|
||||
List<MimeType> mimeTypes = getSupportedMimeTypes();
|
||||
return (!mimeTypes.isEmpty() ? mimeTypes.get(0) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public final Object fromMessage(Message<?> message, Class<?> targetClass) {
|
||||
|
@ -197,10 +185,6 @@ public abstract class AbstractMessageConverter implements SmartMessageConverter
|
|||
return convertFromInternal(message, targetClass, conversionHint);
|
||||
}
|
||||
|
||||
protected boolean canConvertFrom(Message<?> message, Class<?> targetClass) {
|
||||
return (supports(targetClass) && supportsMimeType(message.getHeaders()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public final Message<?> toMessage(Object payload, @Nullable MessageHeaders headers) {
|
||||
|
@ -240,6 +224,11 @@ public abstract class AbstractMessageConverter implements SmartMessageConverter
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
protected boolean canConvertFrom(Message<?> message, Class<?> targetClass) {
|
||||
return (supports(targetClass) && supportsMimeType(message.getHeaders()));
|
||||
}
|
||||
|
||||
protected boolean canConvertTo(Object payload, @Nullable MessageHeaders headers) {
|
||||
return (supports(payload.getClass()) && supportsMimeType(headers));
|
||||
}
|
||||
|
@ -265,6 +254,22 @@ public abstract class AbstractMessageConverter implements SmartMessageConverter
|
|||
return (headers != null && this.contentTypeResolver != null ? this.contentTypeResolver.resolve(headers) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default content type for the payload. Called when
|
||||
* {@link #toMessage(Object, MessageHeaders)} is invoked without
|
||||
* message headers or without a content type header.
|
||||
* <p>By default, this returns the first element of the
|
||||
* {@link #getSupportedMimeTypes() supportedMimeTypes}, if any.
|
||||
* Can be overridden in subclasses.
|
||||
* @param payload the payload being converted to a message
|
||||
* @return the content type, or {@code null} if not known
|
||||
*/
|
||||
@Nullable
|
||||
protected MimeType getDefaultContentType(Object payload) {
|
||||
List<MimeType> mimeTypes = getSupportedMimeTypes();
|
||||
return (!mimeTypes.isEmpty() ? mimeTypes.get(0) : null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whether the given class is supported by this converter.
|
||||
|
@ -307,4 +312,19 @@ public abstract class AbstractMessageConverter implements SmartMessageConverter
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
static Type getResolvedType(Class<?> targetClass, @Nullable Object conversionHint) {
|
||||
if (conversionHint instanceof MethodParameter) {
|
||||
MethodParameter param = (MethodParameter) conversionHint;
|
||||
param = param.nestedIfOptional();
|
||||
if (Message.class.isAssignableFrom(param.getParameterType())) {
|
||||
param = param.nested();
|
||||
}
|
||||
Type genericParameterType = param.getNestedGenericParameterType();
|
||||
Class<?> contextClass = param.getContainingClass();
|
||||
return GenericTypeResolver.resolveType(genericParameterType, contextClass);
|
||||
}
|
||||
return targetClass;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.messaging.converter;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@link MessageConverter} that can read and write JSON
|
||||
* using <a href="https://code.google.com/p/google-gson/">Google Gson</a>.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.3
|
||||
* @see com.google.gson.Gson
|
||||
* @see com.google.gson.GsonBuilder
|
||||
* @see #setGson
|
||||
*/
|
||||
public class GsonMessageConverter extends AbstractJsonMessageConverter {
|
||||
|
||||
private Gson gson;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new {@code GsonMessageConverter} with default configuration.
|
||||
*/
|
||||
public GsonMessageConverter() {
|
||||
this.gson = new Gson();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@code GsonMessageConverter} with the given delegate.
|
||||
* @param gson the Gson instance to use
|
||||
*/
|
||||
public GsonMessageConverter(Gson gson) {
|
||||
Assert.notNull(gson, "A Gson instance is required");
|
||||
this.gson = gson;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the {@code Gson} instance to use.
|
||||
* If not set, a default {@link Gson#Gson() Gson} instance will be used.
|
||||
* <p>Setting a custom-configured {@code Gson} is one way to take further
|
||||
* control of the JSON serialization process.
|
||||
* @see #GsonMessageConverter(Gson)
|
||||
*/
|
||||
public void setGson(Gson gson) {
|
||||
Assert.notNull(gson, "A Gson instance is required");
|
||||
this.gson = gson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured {@code Gson} instance for this converter.
|
||||
*/
|
||||
public Gson getGson() {
|
||||
return this.gson;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Object fromJson(Reader reader, Type resolvedType) {
|
||||
return getGson().fromJson(reader, resolvedType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object fromJson(String payload, Type resolvedType) {
|
||||
return getGson().fromJson(payload, resolvedType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toJson(Object payload, Type resolvedType, Writer writer) {
|
||||
if (resolvedType instanceof ParameterizedType) {
|
||||
getGson().toJson(payload, resolvedType, writer);
|
||||
}
|
||||
else {
|
||||
getGson().toJson(payload, writer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toJson(Object payload, Type resolvedType) {
|
||||
if (resolvedType instanceof ParameterizedType) {
|
||||
return getGson().toJson(payload, resolvedType);
|
||||
}
|
||||
else {
|
||||
return getGson().toJson(payload);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.messaging.converter;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import javax.json.bind.Jsonb;
|
||||
import javax.json.bind.JsonbBuilder;
|
||||
import javax.json.bind.JsonbConfig;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@link MessageConverter} that can read and write JSON
|
||||
* using the <a href="http://json-b.net/">JSON Binding API</a>.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.3
|
||||
* @see javax.json.bind.Jsonb
|
||||
* @see javax.json.bind.JsonbBuilder
|
||||
* @see #setJsonb
|
||||
*/
|
||||
public class JsonbMessageConverter extends AbstractJsonMessageConverter {
|
||||
|
||||
private Jsonb jsonb;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new {@code JsonbMessageConverter} with default configuration.
|
||||
*/
|
||||
public JsonbMessageConverter() {
|
||||
this.jsonb = JsonbBuilder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@code JsonbMessageConverter} with the given configuration.
|
||||
* @param config the {@code JsonbConfig} for the underlying delegate
|
||||
*/
|
||||
public JsonbMessageConverter(JsonbConfig config) {
|
||||
this.jsonb = JsonbBuilder.create(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@code JsonbMessageConverter} with the given delegate.
|
||||
* @param jsonb the Jsonb instance to use
|
||||
*/
|
||||
public JsonbMessageConverter(Jsonb jsonb) {
|
||||
Assert.notNull(jsonb, "A Jsonb instance is required");
|
||||
this.jsonb = jsonb;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the {@code Jsonb} instance to use.
|
||||
* If not set, a default {@code Jsonb} instance will be created.
|
||||
* <p>Setting a custom-configured {@code Jsonb} is one way to take further
|
||||
* control of the JSON serialization process.
|
||||
* @see #JsonbMessageConverter(Jsonb)
|
||||
* @see #JsonbMessageConverter(JsonbConfig)
|
||||
* @see JsonbBuilder
|
||||
*/
|
||||
public void setJsonb(Jsonb jsonb) {
|
||||
Assert.notNull(jsonb, "A Jsonb instance is required");
|
||||
this.jsonb = jsonb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured {@code Jsonb} instance for this converter.
|
||||
*/
|
||||
public Jsonb getJsonb() {
|
||||
return this.jsonb;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Object fromJson(Reader reader, Type resolvedType) {
|
||||
return getJsonb().fromJson(reader, resolvedType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object fromJson(String payload, Type resolvedType) {
|
||||
return getJsonb().fromJson(payload, resolvedType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toJson(Object payload, Type resolvedType, Writer writer) {
|
||||
if (resolvedType instanceof ParameterizedType) {
|
||||
getJsonb().toJson(payload, resolvedType, writer);
|
||||
}
|
||||
else {
|
||||
getJsonb().toJson(payload, writer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toJson(Object payload, Type resolvedType) {
|
||||
if (resolvedType instanceof ParameterizedType) {
|
||||
return getJsonb().toJson(payload, resolvedType);
|
||||
}
|
||||
else {
|
||||
return getJsonb().toJson(payload);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -35,12 +35,12 @@ import com.fasterxml.jackson.databind.MapperFeature;
|
|||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
/**
|
||||
|
@ -139,6 +139,7 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean canConvertFrom(Message<?> message, @Nullable Class<?> targetClass) {
|
||||
if (targetClass == null || !supportsMimeType(message.getHeaders())) {
|
||||
|
@ -206,11 +207,11 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter {
|
|||
@Override
|
||||
@Nullable
|
||||
protected Object convertFromInternal(Message<?> message, Class<?> targetClass, @Nullable Object conversionHint) {
|
||||
JavaType javaType = getJavaType(targetClass, conversionHint);
|
||||
JavaType javaType = this.objectMapper.constructType(getResolvedType(targetClass, conversionHint));
|
||||
Object payload = message.getPayload();
|
||||
Class<?> view = getSerializationView(conversionHint);
|
||||
try {
|
||||
if (targetClass.isInstance(payload)) {
|
||||
if (ClassUtils.isAssignableValue(targetClass, payload)) {
|
||||
return payload;
|
||||
}
|
||||
else if (payload instanceof byte[]) {
|
||||
|
@ -236,21 +237,6 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter {
|
|||
}
|
||||
}
|
||||
|
||||
private JavaType getJavaType(Class<?> targetClass, @Nullable Object conversionHint) {
|
||||
if (conversionHint instanceof MethodParameter) {
|
||||
MethodParameter param = (MethodParameter) conversionHint;
|
||||
param = param.nestedIfOptional();
|
||||
if (Message.class.isAssignableFrom(param.getParameterType())) {
|
||||
param = param.nested();
|
||||
}
|
||||
Type genericParameterType = param.getNestedGenericParameterType();
|
||||
Class<?> contextClass = param.getContainingClass();
|
||||
Type type = GenericTypeResolver.resolveType(genericParameterType, contextClass);
|
||||
return this.objectMapper.getTypeFactory().constructType(type);
|
||||
}
|
||||
return this.objectMapper.constructType(targetClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object convertToInternal(Object payload, @Nullable MessageHeaders headers,
|
||||
|
@ -331,7 +317,7 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter {
|
|||
* @return the JSON encoding to use (never {@code null})
|
||||
*/
|
||||
protected JsonEncoding getJsonEncoding(@Nullable MimeType contentType) {
|
||||
if (contentType != null && (contentType.getCharset() != null)) {
|
||||
if (contentType != null && contentType.getCharset() != null) {
|
||||
Charset charset = contentType.getCharset();
|
||||
for (JsonEncoding encoding : JsonEncoding.values()) {
|
||||
if (charset.name().equals(encoding.getJavaName())) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -34,6 +34,8 @@ import org.springframework.messaging.MessageHandler;
|
|||
import org.springframework.messaging.converter.ByteArrayMessageConverter;
|
||||
import org.springframework.messaging.converter.CompositeMessageConverter;
|
||||
import org.springframework.messaging.converter.DefaultContentTypeResolver;
|
||||
import org.springframework.messaging.converter.GsonMessageConverter;
|
||||
import org.springframework.messaging.converter.JsonbMessageConverter;
|
||||
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
import org.springframework.messaging.converter.StringMessageConverter;
|
||||
|
@ -93,8 +95,20 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
|||
|
||||
private static final String MVC_VALIDATOR_NAME = "mvcValidator";
|
||||
|
||||
private static final boolean jackson2Present = ClassUtils.isPresent(
|
||||
"com.fasterxml.jackson.databind.ObjectMapper", AbstractMessageBrokerConfiguration.class.getClassLoader());
|
||||
private static final boolean jackson2Present;
|
||||
|
||||
private static final boolean gsonPresent;
|
||||
|
||||
private static final boolean jsonbPresent;
|
||||
|
||||
|
||||
static {
|
||||
ClassLoader classLoader = AbstractMessageBrokerConfiguration.class.getClassLoader();
|
||||
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
|
||||
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
|
||||
jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
|
@ -391,6 +405,12 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
|||
if (jackson2Present) {
|
||||
converters.add(createJacksonConverter());
|
||||
}
|
||||
else if (gsonPresent) {
|
||||
converters.add(new GsonMessageConverter());
|
||||
}
|
||||
else if (jsonbPresent) {
|
||||
converters.add(new JsonbMessageConverter());
|
||||
}
|
||||
}
|
||||
return new CompositeMessageConverter(converters);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.messaging.converter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.within;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link GsonMessageConverter}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class GsonMessageConverterTests {
|
||||
|
||||
@Test
|
||||
public void defaultConstructor() {
|
||||
GsonMessageConverter converter = new GsonMessageConverter();
|
||||
assertThat(converter.getSupportedMimeTypes()).contains(new MimeType("application", "json"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessage() {
|
||||
GsonMessageConverter converter = new GsonMessageConverter();
|
||||
String payload = "{\"array\":[\"Foo\",\"Bar\"]," +
|
||||
"\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build();
|
||||
MyBean actual = (MyBean) converter.fromMessage(message, MyBean.class);
|
||||
|
||||
assertThat(actual.getString()).isEqualTo("Foo");
|
||||
assertThat(actual.getNumber()).isEqualTo(42);
|
||||
assertThat(actual.getFraction()).isCloseTo(42F, within(0F));
|
||||
assertThat(actual.getArray()).isEqualTo(new String[]{"Foo", "Bar"});
|
||||
assertThat(actual.isBool()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessageUntyped() {
|
||||
GsonMessageConverter converter = new GsonMessageConverter();
|
||||
String payload = "{\"array\":[\"Foo\",\"Bar\"]," +
|
||||
"\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build();
|
||||
@SuppressWarnings("unchecked")
|
||||
HashMap<String, Object> actual = (HashMap<String, Object>) converter.fromMessage(message, HashMap.class);
|
||||
|
||||
assertThat(actual.get("string")).isEqualTo("Foo");
|
||||
assertThat(actual.get("number")).isEqualTo(42.0);
|
||||
assertThat((Double) actual.get("fraction")).isCloseTo(42D, within(0D));
|
||||
assertThat(actual.get("array")).isEqualTo(Arrays.asList("Foo", "Bar"));
|
||||
assertThat(actual.get("bool")).isEqualTo(Boolean.TRUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessageMatchingInstance() {
|
||||
MyBean myBean = new MyBean();
|
||||
GsonMessageConverter converter = new GsonMessageConverter();
|
||||
Message<?> message = MessageBuilder.withPayload(myBean).build();
|
||||
assertThat(converter.fromMessage(message, MyBean.class)).isSameAs(myBean);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessageInvalidJson() {
|
||||
GsonMessageConverter converter = new GsonMessageConverter();
|
||||
String payload = "FooBar";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build();
|
||||
assertThatExceptionOfType(MessageConversionException.class).isThrownBy(() ->
|
||||
converter.fromMessage(message, MyBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessageValidJsonWithUnknownProperty() {
|
||||
GsonMessageConverter converter = new GsonMessageConverter();
|
||||
String payload = "{\"string\":\"string\",\"unknownProperty\":\"value\"}";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build();
|
||||
MyBean myBean = (MyBean)converter.fromMessage(message, MyBean.class);
|
||||
assertThat(myBean.getString()).isEqualTo("string");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessageToList() throws Exception {
|
||||
GsonMessageConverter converter = new GsonMessageConverter();
|
||||
String payload = "[1, 2, 3, 4, 5, 6, 7, 8, 9]";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build();
|
||||
|
||||
Method method = getClass().getDeclaredMethod("handleList", List.class);
|
||||
MethodParameter param = new MethodParameter(method, 0);
|
||||
Object actual = converter.fromMessage(message, List.class, param);
|
||||
|
||||
assertThat(actual).isNotNull();
|
||||
assertThat(actual).isEqualTo(Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessageToMessageWithPojo() throws Exception {
|
||||
GsonMessageConverter converter = new GsonMessageConverter();
|
||||
String payload = "{\"string\":\"foo\"}";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build();
|
||||
|
||||
Method method = getClass().getDeclaredMethod("handleMessage", Message.class);
|
||||
MethodParameter param = new MethodParameter(method, 0);
|
||||
Object actual = converter.fromMessage(message, MyBean.class, param);
|
||||
|
||||
assertThat(actual instanceof MyBean).isTrue();
|
||||
assertThat(((MyBean) actual).getString()).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toMessage() {
|
||||
GsonMessageConverter converter = new GsonMessageConverter();
|
||||
MyBean payload = new MyBean();
|
||||
payload.setString("Foo");
|
||||
payload.setNumber(42);
|
||||
payload.setFraction(42F);
|
||||
payload.setArray(new String[]{"Foo", "Bar"});
|
||||
payload.setBool(true);
|
||||
|
||||
Message<?> message = converter.toMessage(payload, null);
|
||||
String actual = new String((byte[]) message.getPayload(), StandardCharsets.UTF_8);
|
||||
|
||||
assertThat(actual.contains("\"string\":\"Foo\"")).isTrue();
|
||||
assertThat(actual.contains("\"number\":42")).isTrue();
|
||||
assertThat(actual.contains("fraction\":42.0")).isTrue();
|
||||
assertThat(actual.contains("\"array\":[\"Foo\",\"Bar\"]")).isTrue();
|
||||
assertThat(actual.contains("\"bool\":true")).isTrue();
|
||||
assertThat(message.getHeaders().get(MessageHeaders.CONTENT_TYPE, MimeType.class)).as("Invalid content-type").isEqualTo(new MimeType("application", "json"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toMessageUtf16() {
|
||||
GsonMessageConverter converter = new GsonMessageConverter();
|
||||
MimeType contentType = new MimeType("application", "json", StandardCharsets.UTF_16BE);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put(MessageHeaders.CONTENT_TYPE, contentType);
|
||||
MessageHeaders headers = new MessageHeaders(map);
|
||||
String payload = "H\u00e9llo W\u00f6rld";
|
||||
Message<?> message = converter.toMessage(payload, headers);
|
||||
|
||||
assertThat(new String((byte[]) message.getPayload(), StandardCharsets.UTF_16BE)).isEqualTo("\"" + payload + "\"");
|
||||
assertThat(message.getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(contentType);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toMessageUtf16String() {
|
||||
GsonMessageConverter converter = new GsonMessageConverter();
|
||||
converter.setSerializedPayloadClass(String.class);
|
||||
|
||||
MimeType contentType = new MimeType("application", "json", StandardCharsets.UTF_16BE);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put(MessageHeaders.CONTENT_TYPE, contentType);
|
||||
MessageHeaders headers = new MessageHeaders(map);
|
||||
String payload = "H\u00e9llo W\u00f6rld";
|
||||
Message<?> message = converter.toMessage(payload, headers);
|
||||
|
||||
assertThat(message.getPayload()).isEqualTo("\"" + payload + "\"");
|
||||
assertThat(message.getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(contentType);
|
||||
}
|
||||
|
||||
|
||||
void handleList(List<Long> payload) {
|
||||
}
|
||||
|
||||
void handleMessage(Message<MyBean> message) {
|
||||
}
|
||||
|
||||
|
||||
public static class MyBean {
|
||||
|
||||
private String string;
|
||||
|
||||
private int number;
|
||||
|
||||
private float fraction;
|
||||
|
||||
private String[] array;
|
||||
|
||||
private boolean bool;
|
||||
|
||||
public boolean isBool() {
|
||||
return bool;
|
||||
}
|
||||
|
||||
public void setBool(boolean bool) {
|
||||
this.bool = bool;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return string;
|
||||
}
|
||||
|
||||
public void setString(String string) {
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(int number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public float getFraction() {
|
||||
return fraction;
|
||||
}
|
||||
|
||||
public void setFraction(float fraction) {
|
||||
this.fraction = fraction;
|
||||
}
|
||||
|
||||
public String[] getArray() {
|
||||
return array;
|
||||
}
|
||||
|
||||
public void setArray(String[] array) {
|
||||
this.array = array;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.messaging.converter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.within;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link JsonbMessageConverter}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class JsonbMessageConverterTests {
|
||||
|
||||
@Test
|
||||
public void defaultConstructor() {
|
||||
JsonbMessageConverter converter = new JsonbMessageConverter();
|
||||
assertThat(converter.getSupportedMimeTypes()).contains(new MimeType("application", "json"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessage() {
|
||||
JsonbMessageConverter converter = new JsonbMessageConverter();
|
||||
String payload = "{\"array\":[\"Foo\",\"Bar\"]," +
|
||||
"\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build();
|
||||
MyBean actual = (MyBean) converter.fromMessage(message, MyBean.class);
|
||||
|
||||
assertThat(actual.getString()).isEqualTo("Foo");
|
||||
assertThat(actual.getNumber()).isEqualTo(42);
|
||||
assertThat(actual.getFraction()).isCloseTo(42F, within(0F));
|
||||
assertThat(actual.getArray()).isEqualTo(new String[]{"Foo", "Bar"});
|
||||
assertThat(actual.isBool()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessageUntyped() {
|
||||
JsonbMessageConverter converter = new JsonbMessageConverter();
|
||||
String payload = "{\"array\":[\"Foo\",\"Bar\"]," +
|
||||
"\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build();
|
||||
@SuppressWarnings("unchecked")
|
||||
HashMap<String, Object> actual = (HashMap<String, Object>) converter.fromMessage(message, HashMap.class);
|
||||
|
||||
assertThat(actual.get("string")).isEqualTo("Foo");
|
||||
assertThat(actual.get("number")).isEqualTo(42);
|
||||
assertThat((Double) actual.get("fraction")).isCloseTo(42D, within(0D));
|
||||
assertThat(actual.get("array")).isEqualTo(Arrays.asList("Foo", "Bar"));
|
||||
assertThat(actual.get("bool")).isEqualTo(Boolean.TRUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessageMatchingInstance() {
|
||||
MyBean myBean = new MyBean();
|
||||
JsonbMessageConverter converter = new JsonbMessageConverter();
|
||||
Message<?> message = MessageBuilder.withPayload(myBean).build();
|
||||
assertThat(converter.fromMessage(message, MyBean.class)).isSameAs(myBean);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessageInvalidJson() {
|
||||
JsonbMessageConverter converter = new JsonbMessageConverter();
|
||||
String payload = "FooBar";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build();
|
||||
assertThatExceptionOfType(MessageConversionException.class).isThrownBy(() ->
|
||||
converter.fromMessage(message, MyBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessageValidJsonWithUnknownProperty() {
|
||||
JsonbMessageConverter converter = new JsonbMessageConverter();
|
||||
String payload = "{\"string\":\"string\",\"unknownProperty\":\"value\"}";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build();
|
||||
MyBean myBean = (MyBean)converter.fromMessage(message, MyBean.class);
|
||||
assertThat(myBean.getString()).isEqualTo("string");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessageToList() throws Exception {
|
||||
JsonbMessageConverter converter = new JsonbMessageConverter();
|
||||
String payload = "[1, 2, 3, 4, 5, 6, 7, 8, 9]";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build();
|
||||
|
||||
Method method = getClass().getDeclaredMethod("handleList", List.class);
|
||||
MethodParameter param = new MethodParameter(method, 0);
|
||||
Object actual = converter.fromMessage(message, List.class, param);
|
||||
|
||||
assertThat(actual).isNotNull();
|
||||
assertThat(actual).isEqualTo(Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessageToMessageWithPojo() throws Exception {
|
||||
JsonbMessageConverter converter = new JsonbMessageConverter();
|
||||
String payload = "{\"string\":\"foo\"}";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build();
|
||||
|
||||
Method method = getClass().getDeclaredMethod("handleMessage", Message.class);
|
||||
MethodParameter param = new MethodParameter(method, 0);
|
||||
Object actual = converter.fromMessage(message, MyBean.class, param);
|
||||
|
||||
assertThat(actual instanceof MyBean).isTrue();
|
||||
assertThat(((MyBean) actual).getString()).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toMessage() {
|
||||
JsonbMessageConverter converter = new JsonbMessageConverter();
|
||||
MyBean payload = new MyBean();
|
||||
payload.setString("Foo");
|
||||
payload.setNumber(42);
|
||||
payload.setFraction(42F);
|
||||
payload.setArray(new String[]{"Foo", "Bar"});
|
||||
payload.setBool(true);
|
||||
|
||||
Message<?> message = converter.toMessage(payload, null);
|
||||
String actual = new String((byte[]) message.getPayload(), StandardCharsets.UTF_8);
|
||||
|
||||
assertThat(actual.contains("\"string\":\"Foo\"")).isTrue();
|
||||
assertThat(actual.contains("\"number\":42")).isTrue();
|
||||
assertThat(actual.contains("fraction\":42.0")).isTrue();
|
||||
assertThat(actual.contains("\"array\":[\"Foo\",\"Bar\"]")).isTrue();
|
||||
assertThat(actual.contains("\"bool\":true")).isTrue();
|
||||
assertThat(message.getHeaders().get(MessageHeaders.CONTENT_TYPE, MimeType.class)).as("Invalid content-type").isEqualTo(new MimeType("application", "json"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toMessageUtf16() {
|
||||
JsonbMessageConverter converter = new JsonbMessageConverter();
|
||||
MimeType contentType = new MimeType("application", "json", StandardCharsets.UTF_16BE);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put(MessageHeaders.CONTENT_TYPE, contentType);
|
||||
MessageHeaders headers = new MessageHeaders(map);
|
||||
String payload = "H\u00e9llo W\u00f6rld";
|
||||
Message<?> message = converter.toMessage(payload, headers);
|
||||
|
||||
assertThat(new String((byte[]) message.getPayload(), StandardCharsets.UTF_16BE)).isEqualTo(payload);
|
||||
assertThat(message.getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(contentType);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toMessageUtf16String() {
|
||||
JsonbMessageConverter converter = new JsonbMessageConverter();
|
||||
converter.setSerializedPayloadClass(String.class);
|
||||
|
||||
MimeType contentType = new MimeType("application", "json", StandardCharsets.UTF_16BE);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put(MessageHeaders.CONTENT_TYPE, contentType);
|
||||
MessageHeaders headers = new MessageHeaders(map);
|
||||
String payload = "H\u00e9llo W\u00f6rld";
|
||||
Message<?> message = converter.toMessage(payload, headers);
|
||||
|
||||
assertThat(message.getPayload()).isEqualTo(payload);
|
||||
assertThat(message.getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(contentType);
|
||||
}
|
||||
|
||||
|
||||
void handleList(List<Long> payload) {
|
||||
}
|
||||
|
||||
void handleMessage(Message<MyBean> message) {
|
||||
}
|
||||
|
||||
|
||||
public static class MyBean {
|
||||
|
||||
private String string;
|
||||
|
||||
private int number;
|
||||
|
||||
private float fraction;
|
||||
|
||||
private String[] array;
|
||||
|
||||
private boolean bool;
|
||||
|
||||
public boolean isBool() {
|
||||
return bool;
|
||||
}
|
||||
|
||||
public void setBool(boolean bool) {
|
||||
this.bool = bool;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return string;
|
||||
}
|
||||
|
||||
public void setString(String string) {
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(int number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public float getFraction() {
|
||||
return fraction;
|
||||
}
|
||||
|
||||
public void setFraction(float fraction) {
|
||||
this.fraction = fraction;
|
||||
}
|
||||
|
||||
public String[] getArray() {
|
||||
return array;
|
||||
}
|
||||
|
||||
public void setArray(String[] array) {
|
||||
this.array = array;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -48,10 +48,9 @@ public class MappingJackson2MessageConverterTests {
|
|||
@Test
|
||||
public void defaultConstructor() {
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
assertThat(converter.getSupportedMimeTypes())
|
||||
.contains(new MimeType("application", "json"));
|
||||
assertThat(converter.getSupportedMimeTypes()).contains(new MimeType("application", "json"));
|
||||
assertThat(converter.getObjectMapper().getDeserializationConfig()
|
||||
.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)).isFalse();
|
||||
.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)).isFalse();
|
||||
}
|
||||
|
||||
@Test // SPR-12724
|
||||
|
@ -60,7 +59,7 @@ public class MappingJackson2MessageConverterTests {
|
|||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(mimetype);
|
||||
assertThat(converter.getSupportedMimeTypes()).contains(mimetype);
|
||||
assertThat(converter.getObjectMapper().getDeserializationConfig()
|
||||
.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)).isFalse();
|
||||
.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)).isFalse();
|
||||
}
|
||||
|
||||
@Test // SPR-12724
|
||||
|
@ -70,19 +69,14 @@ public class MappingJackson2MessageConverterTests {
|
|||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(jsonMimetype, xmlMimetype);
|
||||
assertThat(converter.getSupportedMimeTypes()).contains(jsonMimetype, xmlMimetype);
|
||||
assertThat(converter.getObjectMapper().getDeserializationConfig()
|
||||
.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)).isFalse();
|
||||
.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessage() {
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
String payload = "{" +
|
||||
"\"bytes\":\"AQI=\"," +
|
||||
"\"array\":[\"Foo\",\"Bar\"]," +
|
||||
"\"number\":42," +
|
||||
"\"string\":\"Foo\"," +
|
||||
"\"bool\":true," +
|
||||
"\"fraction\":42.0}";
|
||||
String payload = "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"]," +
|
||||
"\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build();
|
||||
MyBean actual = (MyBean) converter.fromMessage(message, MyBean.class);
|
||||
|
||||
|
@ -97,8 +91,8 @@ public class MappingJackson2MessageConverterTests {
|
|||
@Test
|
||||
public void fromMessageUntyped() {
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
String payload = "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],"
|
||||
+ "\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
|
||||
String payload = "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"]," +
|
||||
"\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(StandardCharsets.UTF_8)).build();
|
||||
@SuppressWarnings("unchecked")
|
||||
HashMap<String, Object> actual = (HashMap<String, Object>) converter.fromMessage(message, HashMap.class);
|
||||
|
@ -111,7 +105,7 @@ public class MappingJackson2MessageConverterTests {
|
|||
assertThat(actual.get("bytes")).isEqualTo("AQI=");
|
||||
}
|
||||
|
||||
@Test // gh-22386
|
||||
@Test // gh-22386
|
||||
public void fromMessageMatchingInstance() {
|
||||
MyBean myBean = new MyBean();
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
|
@ -137,7 +131,7 @@ public class MappingJackson2MessageConverterTests {
|
|||
assertThat(myBean.getString()).isEqualTo("string");
|
||||
}
|
||||
|
||||
@Test // SPR-16252
|
||||
@Test // SPR-16252
|
||||
public void fromMessageToList() throws Exception {
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
String payload = "[1, 2, 3, 4, 5, 6, 7, 8, 9]";
|
||||
|
@ -151,7 +145,7 @@ public class MappingJackson2MessageConverterTests {
|
|||
assertThat(actual).isEqualTo(Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L));
|
||||
}
|
||||
|
||||
@Test // SPR-16486
|
||||
@Test // SPR-16486
|
||||
public void fromMessageToMessageWithPojo() throws Exception {
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
String payload = "{\"string\":\"foo\"}";
|
||||
|
@ -198,7 +192,7 @@ public class MappingJackson2MessageConverterTests {
|
|||
String payload = "H\u00e9llo W\u00f6rld";
|
||||
Message<?> message = converter.toMessage(payload, headers);
|
||||
|
||||
assertThat(new String((byte[]) message.getPayload(), StandardCharsets.UTF_16BE)).isEqualTo(("\"" + payload + "\""));
|
||||
assertThat(new String((byte[]) message.getPayload(), StandardCharsets.UTF_16BE)).isEqualTo("\"" + payload + "\"");
|
||||
assertThat(message.getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(contentType);
|
||||
}
|
||||
|
||||
|
@ -214,7 +208,7 @@ public class MappingJackson2MessageConverterTests {
|
|||
String payload = "H\u00e9llo W\u00f6rld";
|
||||
Message<?> message = converter.toMessage(payload, headers);
|
||||
|
||||
assertThat(message.getPayload()).isEqualTo(("\"" + payload + "\""));
|
||||
assertThat(message.getPayload()).isEqualTo("\"" + payload + "\"");
|
||||
assertThat(message.getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(contentType);
|
||||
}
|
||||
|
||||
|
@ -254,9 +248,12 @@ public class MappingJackson2MessageConverterTests {
|
|||
public void jsonViewPayload(@JsonView(MyJacksonView2.class) JacksonViewBean payload) {
|
||||
}
|
||||
|
||||
void handleList(List<Long> payload) {}
|
||||
void handleList(List<Long> payload) {
|
||||
}
|
||||
|
||||
void handleMessage(Message<MyBean> message) {
|
||||
}
|
||||
|
||||
void handleMessage(Message<MyBean> message) {}
|
||||
|
||||
public static class MyBean {
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -42,6 +42,8 @@ import org.springframework.lang.Nullable;
|
|||
import org.springframework.messaging.converter.ByteArrayMessageConverter;
|
||||
import org.springframework.messaging.converter.CompositeMessageConverter;
|
||||
import org.springframework.messaging.converter.DefaultContentTypeResolver;
|
||||
import org.springframework.messaging.converter.GsonMessageConverter;
|
||||
import org.springframework.messaging.converter.JsonbMessageConverter;
|
||||
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
|
||||
import org.springframework.messaging.converter.StringMessageConverter;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
|
@ -113,11 +115,18 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser {
|
|||
|
||||
private static final boolean jackson2Present;
|
||||
|
||||
private static final boolean gsonPresent;
|
||||
|
||||
private static final boolean jsonbPresent;
|
||||
|
||||
private static final boolean javaxValidationPresent;
|
||||
|
||||
static {
|
||||
ClassLoader classLoader = MessageBrokerBeanDefinitionParser.class.getClassLoader();
|
||||
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader);
|
||||
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
|
||||
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
|
||||
jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
|
||||
javaxValidationPresent = ClassUtils.isPresent("javax.validation.Validator", classLoader);
|
||||
}
|
||||
|
||||
|
@ -502,6 +511,12 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser {
|
|||
jacksonConverterDef.getPropertyValues().add("objectMapper", jacksonFactoryDef);
|
||||
converters.add(jacksonConverterDef);
|
||||
}
|
||||
else if (gsonPresent) {
|
||||
converters.add(new RootBeanDefinition(GsonMessageConverter.class));
|
||||
}
|
||||
else if (jsonbPresent) {
|
||||
converters.add(new RootBeanDefinition(JsonbMessageConverter.class));
|
||||
}
|
||||
}
|
||||
ConstructorArgumentValues cargs = new ConstructorArgumentValues();
|
||||
cargs.addIndexedArgumentValue(0, converters);
|
||||
|
|
Loading…
Reference in New Issue