11 changed files with 971 additions and 68 deletions
@ -0,0 +1,131 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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