INT-4001 Disable some Jackson features by default

JIRA: https://jira.spring.io/browse/INT-4001

Since all other Spring Framework components use the Jackson's `ObjectMapper` configuration by default as:
```
this.objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
```
It would be better to be consistent in Spring Integration, too.

* Add mention options to the `Jackson2JsonObjectMapper` default's `ObjectMapper`
* Add on more constructor into the `Jackson2JsonMessageParser` to allow to inject customized `Jackson2JsonObjectMapper`
* Modify a couple tests to reflect the change
* Reformat code style in the `AbstractJsonInboundMessageMapperTests` for better readability

**The Migration Guide note follows up**
This commit is contained in:
Artem Bilan
2016-04-20 13:37:30 -04:00
committed by Gary Russell
parent 256a7292ef
commit 1dc4ebe4d1
4 changed files with 88 additions and 39 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2016 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.
@@ -39,7 +39,11 @@ import com.fasterxml.jackson.core.JsonToken;
public class Jackson2JsonMessageParser extends AbstractJacksonJsonMessageParser<JsonParser> {
public Jackson2JsonMessageParser() {
super(new Jackson2JsonObjectMapper());
this(new Jackson2JsonObjectMapper());
}
public Jackson2JsonMessageParser(Jackson2JsonObjectMapper objectMapper) {
super(objectMapper);
}
@Override

View File

@@ -25,18 +25,26 @@ import java.net.URL;
import java.util.Collection;
import java.util.Map;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.integration.mapping.support.JsonHeaders;
import org.springframework.util.Assert;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Jackson 2 JSON-processor (@link https://github.com/FasterXML) {@linkplain JsonObjectMapper} implementation.
* Delegates <code>toJson</code> and <code>fromJson</code>
* to the {@linkplain com.fasterxml.jackson.databind.ObjectMapper}
* <p>
* It customizes Jackson's default properties with the following ones:
* <ul>
* <li>{@link MapperFeature#DEFAULT_VIEW_INCLUSION} is disabled</li>
* <li>{@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} is disabled</li>
* </ul>
*
* @author Artem Bilan
* @since 3.0
@@ -47,6 +55,8 @@ public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper<Js
public Jackson2JsonObjectMapper() {
this.objectMapper = new ObjectMapper();
this.objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
public Jackson2JsonObjectMapper(ObjectMapper objectMapper) {

View File

@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -51,16 +52,20 @@ public abstract class AbstractJsonInboundMessageMapperTests {
private final JsonObjectMapper<?, ?> mapper = JsonObjectMapperProvider.newInstance();
@Factory
public static Matcher<Message<?>> sameExceptImmutableHeaders(Message<?> operand) {
return new MessageMatcher(operand);
}
public static Matcher<Message<?>> sameExceptImmutableHeaders(Message<?> operand) {
return new MessageMatcher(operand);
}
@Test
public void testToMessageWithHeadersAndStringPayload() throws Exception {
UUID id = UUID.randomUUID();
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id + "\",\"foo\":123,\"bar\":\"abc\"},\"payload\":\"myPayloadStuff\"}";
Message<String> expected = MessageBuilder.withPayload("myPayloadStuff").setHeader("foo", 123).setHeader("bar", "abc").build();
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id +
"\",\"foo\":123,\"bar\":\"abc\"},\"payload\":\"myPayloadStuff\"}";
Message<String> expected = MessageBuilder.withPayload("myPayloadStuff")
.setHeader("foo", 123)
.setHeader("bar", "abc")
.build();
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(String.class, getParser());
Message<?> result = mapper.toMessage(jsonMessage);
assertThat(result, sameExceptImmutableHeaders(expected));
@@ -80,8 +85,12 @@ public abstract class AbstractJsonInboundMessageMapperTests {
public void testToMessageWithHeadersAndBeanPayload() throws Exception {
TestBean bean = new TestBean();
UUID id = UUID.randomUUID();
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id + "\",\"foo\":123,\"bar\":\"abc\"},\"payload\":" + getBeanAsJson(bean) + "}";
Message<TestBean> expected = MessageBuilder.withPayload(bean).setHeader("foo", 123).setHeader("bar", "abc").build();
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id +
"\",\"foo\":123,\"bar\":\"abc\"},\"payload\":" + getBeanAsJson(bean) + "}";
Message<TestBean> expected = MessageBuilder.withPayload(bean)
.setHeader("foo", 123)
.setHeader("bar", "abc")
.build();
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(TestBean.class, getParser());
Message<?> result = mapper.toMessage(jsonMessage);
assertThat(result, sameExceptImmutableHeaders(expected));
@@ -101,8 +110,11 @@ public abstract class AbstractJsonInboundMessageMapperTests {
public void testToMessageWithBeanHeaderAndStringPayload() throws Exception {
TestBean bean = new TestBean();
UUID id = UUID.randomUUID();
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id + "\", \"myHeader\":" + getBeanAsJson(bean) + "},\"payload\":\"myPayloadStuff\"}";
Message<String> expected = MessageBuilder.withPayload("myPayloadStuff").setHeader("myHeader", bean).build();
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id + "\", \"myHeader\":" +
getBeanAsJson(bean) + "},\"payload\":\"myPayloadStuff\"}";
Message<String> expected = MessageBuilder.withPayload("myPayloadStuff")
.setHeader("myHeader", bean)
.build();
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(String.class, getParser());
Map<String, Class<?>> headerTypes = new HashMap<String, Class<?>>();
headerTypes.put("myHeader", TestBean.class);
@@ -114,10 +126,18 @@ public abstract class AbstractJsonInboundMessageMapperTests {
@Test
public void testToMessageWithHeadersAndListOfStringsPayload() throws Exception {
UUID id = UUID.randomUUID();
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id + "\",\"foo\":123,\"bar\":\"abc\"},\"payload\":[\"myPayloadStuff1\",\"myPayloadStuff2\",\"myPayloadStuff3\"]}";
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id +
"\",\"foo\":123,\"bar\":\"abc\"}," +
"\"payload\":[\"myPayloadStuff1\",\"myPayloadStuff2\",\"myPayloadStuff3\"]}";
List<String> expectedList = Arrays.asList("myPayloadStuff1", "myPayloadStuff2", "myPayloadStuff3");
Message<List<String>> expected = MessageBuilder.withPayload(expectedList).setHeader("foo", 123).setHeader("bar", "abc").build();
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(new ParameterizedTypeReference<List<String>>() { }.getType(), getParser());
Message<List<String>> expected = MessageBuilder.withPayload(expectedList)
.setHeader("foo", 123)
.setHeader("bar", "abc")
.build();
Type type = new ParameterizedTypeReference<List<String>>() {
}.getType();
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(type, getParser());
Message<?> result = mapper.toMessage(jsonMessage);
assertThat(result, sameExceptImmutableHeaders(expected));
}
@@ -127,10 +147,18 @@ public abstract class AbstractJsonInboundMessageMapperTests {
TestBean bean1 = new TestBean();
TestBean bean2 = new TestBean();
UUID id = UUID.randomUUID();
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id + "\",\"foo\":123,\"bar\":\"abc\"},\"payload\":[" + getBeanAsJson(bean1) + "," + getBeanAsJson(bean2) + "]}";
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id +
"\",\"foo\":123,\"bar\":\"abc\"},\"payload\":[" + getBeanAsJson(bean1) +
"," + getBeanAsJson(bean2) + "]}";
List<TestBean> expectedList = Arrays.asList(bean1, bean2);
Message<List<TestBean>> expected = MessageBuilder.withPayload(expectedList).setHeader("foo", 123).setHeader("bar", "abc").build();
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(new ParameterizedTypeReference<List<TestBean>>() { }.getType(), getParser());
Message<List<TestBean>> expected = MessageBuilder.withPayload(expectedList)
.setHeader("foo", 123)
.setHeader("bar", "abc")
.build();
Type type = new ParameterizedTypeReference<List<TestBean>>() {
}.getType();
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(type, getParser());
Message<?> result = mapper.toMessage(jsonMessage);
assertThat(result, sameExceptImmutableHeaders(expected));
}
@@ -138,8 +166,12 @@ public abstract class AbstractJsonInboundMessageMapperTests {
@Test
public void testToMessageWithPayloadAndHeadersReversed() throws Exception {
UUID id = UUID.randomUUID();
String jsonMessage = "{\"payload\":\"myPayloadStuff\",\"headers\":{\"timestamp\":1,\"id\":\"" + id + "\",\"foo\":123,\"bar\":\"abc\"}}";
Message<String> expected = MessageBuilder.withPayload("myPayloadStuff").setHeader("foo", 123).setHeader("bar", "abc").build();
String jsonMessage = "{\"payload\":\"myPayloadStuff\",\"headers\":{\"timestamp\":1,\"id\":\"" +
id + "\",\"foo\":123,\"bar\":\"abc\"}}";
Message<String> expected = MessageBuilder.withPayload("myPayloadStuff")
.setHeader("foo", 123)
.setHeader("bar", "abc")
.build();
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(String.class, getParser());
Message<?> result = mapper.toMessage(jsonMessage);
assertThat(result, sameExceptImmutableHeaders(expected));
@@ -188,26 +220,23 @@ public abstract class AbstractJsonInboundMessageMapperTests {
}
@Test
public void testToMessageInvalidFormatHeadersAndBeanPayloadWithMapToPayload() throws Exception {
public void testToMessageInvalidFormatHeadersAndBeanPayloadWithMapToPayloadNotFail() throws Exception {
TestBean bean = new TestBean();
UUID id = UUID.randomUUID();
String jsonMessage = "{\"headers\":{\"$timestamp\":1,\"$id\":\"" + id + "\"},\"payload\":" + getBeanAsJson(bean) + "}";
String jsonMessage = "{\"headers\":{\"$timestamp\":1,\"$id\":\"" + id + "\"},\"payload\":" +
getBeanAsJson(bean) + "}";
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(TestBean.class, getParser());
mapper.setMapToPayload(true);
try {
mapper.toMessage(jsonMessage);
fail();
}
catch (IllegalArgumentException ex) {
//Expected
}
Message<?> message = mapper.toMessage(jsonMessage);
assertEquals(bean, message.getPayload());
}
@Test
public void testToMessageWithHeadersAndPayloadTypeMappingFailure() throws Exception {
TestBean bean = new TestBean();
UUID id = UUID.randomUUID();
String jsonMessage = "{\"headers\":{\"$timestamp\":1,\"$id\":\"" + id + "\"},\"payload\":" + getBeanAsJson(bean) + "}";
String jsonMessage = "{\"headers\":{\"$timestamp\":1,\"$id\":\"" + id + "\"},\"payload\":" +
getBeanAsJson(bean) + "}";
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(Long.class, getParser());
try {
mapper.toMessage(jsonMessage);
@@ -222,7 +251,8 @@ public abstract class AbstractJsonInboundMessageMapperTests {
public void testToMessageWithBeanHeaderTypeMappingFailure() throws Exception {
TestBean bean = new TestBean();
UUID id = UUID.randomUUID();
String jsonMessage = "{\"headers\":{\"$timestamp\":1,\"$id\":\"" + id + "\",\"myHeader\":" + getBeanAsJson(bean) + "},\"payload\":\"myPayloadStuff\"}";
String jsonMessage = "{\"headers\":{\"$timestamp\":1,\"$id\":\"" + id + "\",\"myHeader\":" +
getBeanAsJson(bean) + "},\"payload\":\"myPayloadStuff\"}";
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(String.class, getParser());
Map<String, Class<?>> headerTypes = new HashMap<String, Class<?>>();
headerTypes.put("myHeader", Long.class);
@@ -238,8 +268,9 @@ public abstract class AbstractJsonInboundMessageMapperTests {
private String getBeanAsJson(TestBean bean) throws Exception {
return mapper.toJson(bean);
return this.mapper.toJson(bean);
}
protected abstract JsonMessageParser<?> getParser();
}

View File

@@ -39,8 +39,11 @@ public class JsonToObjectTransformerTests {
@Test
public void objectPayload() throws Exception {
JsonToObjectTransformer transformer = new JsonToObjectTransformer(TestPerson.class);
String jsonString = "{\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":42,\"address\":{\"number\":123,\"street\":\"Main Street\"}}";
Message<?> message = transformer.transform(new GenericMessage<String>(jsonString));
// 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\"}";
Message<?> message = transformer.transform(new GenericMessage<>(jsonString));
TestPerson person = (TestPerson) message.getPayload();
assertEquals("John", person.getFirstName());
assertEquals("Doe", person.getLastName());
@@ -68,7 +71,8 @@ public class JsonToObjectTransformerTests {
@Test
public void testBoonJsonObjectMapper() throws Exception {
JsonToObjectTransformer transformer = new JsonToObjectTransformer(TestPerson.class, new BoonJsonObjectMapper());
String jsonString = "{\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":42,\"address\":{\"number\":123,\"street\":\"Main Street\"}}";
String jsonString = "{\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":42," +
"\"address\":{\"number\":123,\"street\":\"Main Street\"}}";
Message<?> message = transformer.transform(new GenericMessage<String>(jsonString));
TestPerson person = (TestPerson) message.getPayload();
assertEquals("John", person.getFirstName());