11 changed files with 971 additions and 68 deletions
@ -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); |
||||||
|
|
||||||
|
} |
||||||
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue