Support generics in the JsonToObjectTransformer (#2819)

* Support generics in the `JsonToObjectTransformer`

* Deprecate `JsonObjectMapperAdapter` if favor of `default` methods in
the `JsonObjectMapper`
* Introduce `JsonObjectMapper.fromJson(Object, ResolvableType)` to
support generics during deserialization
* Add `JsonHeaders.RESOLVABLE_TYPE` header handling for the
`ResolvableType` management
* Add `ResolvableType` argument into the `JsonToObjectTransformer`
* Change the `JsonToObjectTransformer` logic to consult request message
headers first
* Add `ResolvableType`-based factory method into the `Transformers`
* Document the change

* * Use `0` for conversion to array for headers to remove
in the `JsonToObjectTransformer`
This commit is contained in:
Artem Bilan
2019-03-18 17:15:32 -04:00
committed by Gary Russell
parent 8f86ef918c
commit 5421574ef5
13 changed files with 285 additions and 107 deletions

View File

@@ -23,11 +23,11 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.ResolvableType;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.json.Jackson2JsonObjectMapper;
import org.springframework.integration.support.json.JsonObjectMapper;
import org.springframework.integration.support.json.JsonObjectMapperAdapter;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
@@ -104,12 +104,18 @@ public class JsonToObjectTransformerParserTests {
assertThat(result.getJson()).isEqualTo(jsonString);
}
@SuppressWarnings("rawtypes")
static class CustomJsonObjectMapper extends JsonObjectMapperAdapter {
static class CustomJsonObjectMapper implements JsonObjectMapper<Object, Object> {
@Override
public Object fromJson(Object json, Class valueType) {
return new TestJsonContainer((String) json);
@SuppressWarnings("unchecked")
public <T> T fromJson(Object json, Class<T> valueType) {
return (T) new TestJsonContainer((String) json);
}
@Override
@SuppressWarnings("unchecked")
public <T> T fromJson(Object json, ResolvableType valueType) {
return (T) new TestJsonContainer((String) json);
}
}

View File

@@ -18,8 +18,12 @@ package org.springframework.integration.json;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import org.junit.Test;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.integration.support.json.Jackson2JsonObjectMapper;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
@@ -31,24 +35,36 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @author Mark Fisher
* @author Artem Bilan
* @author Gary Russell
*
* @since 2.0
*/
public class JsonToObjectTransformerTests {
@Test
public void objectPayload() {
JsonToObjectTransformer transformer = new JsonToObjectTransformer(TestPerson.class);
JsonToObjectTransformer transformer =
new JsonToObjectTransformer(
ResolvableType.forType(new ParameterizedTypeReference<List<TestPerson>>() { }));
// Since DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES is disabled by default
// (see Jackson2JsonObjectMapper)
// the extra "foo" property is ignored.
String jsonString = "{\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":42," +
"\"address\":{\"number\":123,\"street\":\"Main Street\"}, \"foo\":\"bar\"}";
String jsonString = "[{\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":42," +
"\"address\":{\"number\":123,\"street\":\"Main Street\"}, \"foo\":\"bar\"}]";
Message<?> message = transformer.transform(new GenericMessage<>(jsonString));
TestPerson person = (TestPerson) message.getPayload();
assertThat(person.getFirstName()).isEqualTo("John");
assertThat(person.getLastName()).isEqualTo("Doe");
assertThat(person.getAge()).isEqualTo(42);
assertThat(person.getAddress().toString()).isEqualTo("123 Main Street");
assertThat(message)
.extracting(Message::getPayload)
.isInstanceOf(List.class)
.asList()
.hasSize(1)
.element(0)
.isInstanceOf(TestPerson.class)
.satisfies((actual) -> {
TestPerson bean = (TestPerson) actual;
assertThat(bean).extracting(TestPerson::getFirstName).isEqualTo("John");
assertThat(bean).extracting(TestPerson::getLastName).isEqualTo("Doe");
assertThat(bean).extracting(TestPerson::getAge).isEqualTo(42);
assertThat(bean).extracting(TestPerson::getAddress).asString().isEqualTo("123 Main Street");
});
}
@Test

View File

@@ -33,7 +33,7 @@ import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.support.DefaultMessageBuilderFactory;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.json.Jackson2JsonObjectMapper;
import org.springframework.integration.support.json.JsonObjectMapperAdapter;
import org.springframework.integration.support.json.JsonObjectMapper;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
@@ -174,7 +174,7 @@ public class ObjectToJsonTransformerParserTests {
assertThat(expression.getValue(evaluationContext, payload, Boolean.class)).isTrue();
}
static class CustomJsonObjectMapper extends JsonObjectMapperAdapter<Object, Object> {
static class CustomJsonObjectMapper implements JsonObjectMapper<Object, Object> {
@Override
public String toJson(Object value) {

View File

@@ -183,11 +183,11 @@ public class ObjectToJsonTransformerTests {
List<String> list = Collections.singletonList(null);
Message<?> out = transformer.transform(new GenericMessage<>(list));
assertThat(out.getHeaders().get(JsonHeaders.TYPE_ID).toString()).contains("SingletonList");
assertThat(out.getHeaders().get(JsonHeaders.CONTENT_TYPE_ID)).isEqualTo(Object.class);
assertThat(out.getHeaders()).doesNotContainKey(JsonHeaders.CONTENT_TYPE_ID);
Map<String, String> map = Collections.singletonMap("foo", null);
out = transformer.transform(new GenericMessage<>(map));
assertThat(out.getHeaders().get(JsonHeaders.TYPE_ID).toString()).contains("SingletonMap");
assertThat(out.getHeaders().get(JsonHeaders.CONTENT_TYPE_ID)).isEqualTo(Object.class);
assertThat(out.getHeaders()).doesNotContainKey(JsonHeaders.CONTENT_TYPE_ID);
assertThat(out.getHeaders().get(JsonHeaders.KEY_TYPE_ID)).isEqualTo(String.class);
}