INT-3130: Add result-type to <o-to-j-transformer>

JIRA: https://jira.springsource.org/browse/INT-3130

* add `result-type` attribute to the `<object-to-json-transformer>`
based on enumeration values: `STRING`, `NODE`
* Provide implementations for methods to convert value to the node representation
* Refactoring according new generic option in the `JsonObjectMapper`
* Add tests and documentation

Need Migration Guide note according this refactoring

INT-3130: remove some deprecation and JavaDocs

Addressed PR comments

Doc Polishing
This commit is contained in:
Artem Bilan
2014-01-27 15:58:29 +02:00
committed by Gary Russell
parent 9005938ab5
commit 062e7bc9d2
21 changed files with 165 additions and 199 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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,12 +16,12 @@
package org.springframework.integration.config.xml;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.json.ObjectToJsonTransformer;
import org.springframework.util.StringUtils;
/**
* @author Mark Fisher
@@ -42,6 +42,10 @@ public class ObjectToJsonTransformerParser extends AbstractTransformerParser {
if (StringUtils.hasText(objectMapper)) {
builder.addConstructorArgReference(objectMapper);
}
String resultType = element.getAttribute("result-type");
if (StringUtils.hasText(resultType)) {
builder.addConstructorArgValue(resultType);
}
if (element.hasAttribute("content-type")){
builder.addPropertyValue("contentType", element.getAttribute("content-type"));
}

View File

@@ -23,8 +23,6 @@ import org.springframework.integration.support.json.JacksonJsonObjectMapperProvi
import org.springframework.integration.support.json.JsonObjectMapper;
import org.springframework.integration.transformer.AbstractTransformer;
import org.springframework.messaging.Message;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Transformer implementation that converts a JSON string payload into an instance of the provided target Class.
@@ -43,7 +41,7 @@ public class JsonToObjectTransformer extends AbstractTransformer implements Bean
private final Class<?> targetClass;
private final JsonObjectMapper<?> jsonObjectMapper;
private final JsonObjectMapper<?, ?> jsonObjectMapper;
public JsonToObjectTransformer() {
this((Class<?>) null);
@@ -53,40 +51,11 @@ public class JsonToObjectTransformer extends AbstractTransformer implements Bean
this(targetClass, null);
}
/**
* Backward compatibility - allows existing configurations using Jackson 1.x to inject
* an ObjectMapper directly.
*
* @param targetClass The target class.
* @param objectMapper The object mapper.
* @throws ClassNotFoundException When the target class is not found.
*
* @deprecated in favor of {@link #JsonToObjectTransformer(Class, JsonObjectMapper)}
*/
@Deprecated
public JsonToObjectTransformer(Class<?> targetClass, Object objectMapper) throws ClassNotFoundException {
this.targetClass = targetClass;
if (objectMapper != null) {
try {
Class<?> objectMapperClass = ClassUtils.forName("org.codehaus.jackson.map.ObjectMapper", ClassUtils.getDefaultClassLoader());
Assert.isTrue(objectMapperClass.isAssignableFrom(objectMapper.getClass()));
this.jsonObjectMapper = new org.springframework.integration.support.json.JacksonJsonObjectMapper(
(org.codehaus.jackson.map.ObjectMapper) objectMapper);
}
catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
}
else {
this.jsonObjectMapper = JacksonJsonObjectMapperProvider.newInstance();
}
}
public JsonToObjectTransformer(JsonObjectMapper<?> jsonObjectMapper) {
public JsonToObjectTransformer(JsonObjectMapper<?, ?> jsonObjectMapper) {
this(null, jsonObjectMapper);
}
public JsonToObjectTransformer(Class<?> targetClass, JsonObjectMapper<?> jsonObjectMapper) {
public JsonToObjectTransformer(Class<?> targetClass, JsonObjectMapper<?, ?> jsonObjectMapper) {
this.targetClass = targetClass;
this.jsonObjectMapper = (jsonObjectMapper != null) ? jsonObjectMapper : JacksonJsonObjectMapperProvider.newInstance();
}

View File

@@ -22,7 +22,6 @@ import org.springframework.integration.transformer.AbstractTransformer;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.util.StringUtils;
@@ -42,43 +41,37 @@ import org.springframework.util.StringUtils;
*/
public class ObjectToJsonTransformer extends AbstractTransformer {
public static enum ResultType {
STRING, NODE
}
public static final String JSON_CONTENT_TYPE = "application/json";
private final JsonObjectMapper<?> jsonObjectMapper;
private final JsonObjectMapper<?, ?> jsonObjectMapper;
private final ResultType resultType;
private volatile String contentType = JSON_CONTENT_TYPE;
private volatile boolean contentTypeExplicitlySet = false;
/**
* Backward compatibility - allows existing configurations using Jackson 1.x to inject
* an ObjectMapper directly.
*
* @param objectMapper The object mapper.
*
* @deprecated in favor of {@link #ObjectToJsonTransformer(JsonObjectMapper)}
*/
@Deprecated
public ObjectToJsonTransformer(Object objectMapper) {
Assert.notNull(objectMapper, "objectMapper must not be null");
try {
Class<?> objectMapperClass = ClassUtils.forName("org.codehaus.jackson.map.ObjectMapper", ClassUtils.getDefaultClassLoader());
Assert.isTrue(objectMapperClass.isAssignableFrom(objectMapper.getClass()));
this.jsonObjectMapper = new org.springframework.integration.support.json.JacksonJsonObjectMapper(
(org.codehaus.jackson.map.ObjectMapper) objectMapper);
}
catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
}
public ObjectToJsonTransformer(JsonObjectMapper<?> jsonObjectMapper) {
Assert.notNull(jsonObjectMapper, "jsonObjectMapper must not be null");
this.jsonObjectMapper = jsonObjectMapper;
}
public ObjectToJsonTransformer() {
this.jsonObjectMapper = JacksonJsonObjectMapperProvider.newInstance();
this(JacksonJsonObjectMapperProvider.newInstance());
}
public ObjectToJsonTransformer(JsonObjectMapper<?, ?> jsonObjectMapper) {
this(jsonObjectMapper, ResultType.STRING);
}
public ObjectToJsonTransformer(ResultType resultType) {
this(JacksonJsonObjectMapperProvider.newInstance(), resultType);
}
public ObjectToJsonTransformer(JsonObjectMapper<?, ?> jsonObjectMapper, ResultType resultType) {
Assert.notNull(jsonObjectMapper, "jsonObjectMapper must not be null");
Assert.notNull(resultType, "'resultType' must not be null");
this.jsonObjectMapper = jsonObjectMapper;
this.resultType = resultType;
}
/**
@@ -95,8 +88,10 @@ public class ObjectToJsonTransformer extends AbstractTransformer {
@Override
protected Object doTransform(Message<?> message) throws Exception {
String payload = this.jsonObjectMapper.toJson(message.getPayload());
MessageBuilder<String> messageBuilder = MessageBuilder.withPayload(payload);
Object payload = ResultType.STRING.equals(this.resultType)
? this.jsonObjectMapper.toJson(message.getPayload())
: this.jsonObjectMapper.toJsonNode(message.getPayload());
MessageBuilder<Object> messageBuilder = MessageBuilder.withPayload(payload);
LinkedCaseInsensitiveMap<Object> headers = new LinkedCaseInsensitiveMap<Object>();
headers.putAll(message.getHeaders());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -30,11 +30,11 @@ import org.springframework.messaging.Message;
*/
abstract class AbstractJacksonJsonMessageParser<P> implements JsonInboundMessageMapper.JsonMessageParser<P> {
private final JsonObjectMapper<P> objectMapper;
private final JsonObjectMapper<?, P> objectMapper;
private volatile JsonInboundMessageMapper messageMapper;
protected AbstractJacksonJsonMessageParser(JsonObjectMapper<P> objectMapper) {
protected AbstractJacksonJsonMessageParser(JsonObjectMapper<?, P> objectMapper) {
this.objectMapper = objectMapper;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2014 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.
@@ -31,10 +31,14 @@ import org.springframework.util.ClassUtils;
/**
* Base class for Jackson {@link JsonObjectMapper} implementations.
*
* @param <N> - The expected type of JSON Node.
* @param <P> - The expected type of JSON Parser.
* @param <J> - The expected type of Java Type representation.
*
* @author Artem Bilan
* @since 3.0
*/
public abstract class AbstractJacksonJsonObjectMapper<P, J> implements JsonObjectMapper<P>, BeanClassLoaderAware {
public abstract class AbstractJacksonJsonObjectMapper<N, P, J> implements JsonObjectMapper<N, P>, BeanClassLoaderAware {
protected static final Collection<Class<?>> supportedJsonTypes =
Arrays.<Class<?>> asList(String.class, byte[].class, File.class, URL.class, InputStream.class, Reader.class);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2014 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.
@@ -30,6 +30,7 @@ import org.springframework.util.Assert;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
@@ -40,7 +41,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @author Artem Bilan
* @since 3.0
*/
public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper<JsonParser, JavaType> {
public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper<JsonNode, JsonParser, JavaType> {
private final ObjectMapper objectMapper;
@@ -63,6 +64,11 @@ public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper<Js
this.objectMapper.writeValue(writer, value);
}
@Override
public JsonNode toJsonNode(Object value) throws Exception {
return this.objectMapper.valueToTree(value);
}
@Override
protected <T> T fromJson(Object json, JavaType type) throws Exception {
if (json instanceof String) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -25,6 +25,7 @@ import java.net.URL;
import java.util.Collection;
import java.util.Map;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.JavaType;
@@ -43,7 +44,7 @@ import org.springframework.util.Assert;
* @since 3.0
*/
@Deprecated
public class JacksonJsonObjectMapper extends AbstractJacksonJsonObjectMapper<JsonParser, JavaType> {
public class JacksonJsonObjectMapper extends AbstractJacksonJsonObjectMapper<JsonNode, JsonParser, JavaType> {
private final ObjectMapper objectMapper;
@@ -66,6 +67,11 @@ public class JacksonJsonObjectMapper extends AbstractJacksonJsonObjectMapper<Jso
this.objectMapper.writeValue(writer, value);
}
@Override
public JsonNode toJsonNode(Object value) throws Exception {
return this.objectMapper.valueToTree(value);
}
@Override
public <T> T fromJson(JsonParser parser, Type valueType) throws Exception {
return this.objectMapper.readValue(parser, this.constructType(valueType));

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -32,7 +32,7 @@ package org.springframework.integration.support.json;
public final class JacksonJsonObjectMapperProvider {
@SuppressWarnings("deprecation")
public static JsonObjectMapper<?> newInstance() {
public static JsonObjectMapper<?, ?> newInstance() {
if (JacksonJsonUtils.isJackson2Present()) {
return new Jackson2JsonObjectMapper();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2013-2014 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.
@@ -23,16 +23,21 @@ import java.util.Map;
/**
* Strategy interface to convert an Object to/from the JSON representation.
*
* @param <N> - The expected type of JSON Node.
* @param <P> - The expected type of JSON Parser.
*
* @author Artem Bilan
* @since 3.0
*
*/
public interface JsonObjectMapper<P> {
public interface JsonObjectMapper<N, P> {
String toJson(Object value) throws Exception;
void toJson(Object value, Writer writer) throws Exception;
N toJsonNode(Object value) throws Exception;
<T> T fromJson(Object json, Class<T> valueType) throws Exception;
<T> T fromJson(Object json, Map<String, Object> javaTypes) throws Exception;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2013-2014 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.
@@ -27,7 +27,7 @@ import java.util.Map;
* @author Artem Bilan
* @since 3.0
*/
public abstract class JsonObjectMapperAdapter<P> implements JsonObjectMapper<P> {
public abstract class JsonObjectMapperAdapter<N, P> implements JsonObjectMapper<N, P> {
@Override
public String toJson(Object value) throws Exception {
@@ -38,6 +38,11 @@ public abstract class JsonObjectMapperAdapter<P> implements JsonObjectMapper<P>
public void toJson(Object value, Writer writer) throws Exception {
}
@Override
public N toJsonNode(Object value) throws Exception {
return null;
}
@Override
public <T> T fromJson(Object json, Class<T> valueType) throws Exception {
return null;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -32,13 +32,13 @@ public class JsonOutboundMessageMapper implements OutboundMessageMapper<String>
private volatile boolean shouldExtractPayload = false;
private volatile JsonObjectMapper<?> jsonObjectMapper;
private volatile JsonObjectMapper<?, ?> jsonObjectMapper;
public JsonOutboundMessageMapper() {
this(JacksonJsonObjectMapperProvider.newInstance());
}
public JsonOutboundMessageMapper(JsonObjectMapper<?> jsonObjectMapper) {
public JsonOutboundMessageMapper(JsonObjectMapper<?, ?> jsonObjectMapper) {
Assert.notNull(jsonObjectMapper, "jsonObjectMapper must not be null");
this.jsonObjectMapper = jsonObjectMapper;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -54,7 +54,7 @@ import org.springframework.util.StringUtils;
*/
public class ObjectToMapTransformer extends AbstractPayloadTransformer<Object, Map<?,?>> {
private final JsonObjectMapper<?> jsonObjectMapper = JacksonJsonObjectMapperProvider.newInstance();
private final JsonObjectMapper<?, ?> jsonObjectMapper = JacksonJsonObjectMapperProvider.newInstance();
private volatile boolean shouldFlattenKeys = true;

View File

@@ -2263,17 +2263,37 @@
This Jackson 1 ObjectMapper backward compatibility is deprecated
and will be removed in the Spring Integration 3.1 or above.
</xsd:documentation>
<!-- TODO: Revert in the Spring Integration 3.1 or above after removing Jackson 1 backward compatibility
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.support.json.JsonObjectMapper" />
</tool:annotation>
</xsd:appinfo>-->
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="result-type" default="STRING">
<xsd:annotation>
<xsd:documentation>
The type of the JSON transformation result. 'STRING' and 'NODE' values are allowed.
If 'NODE', the JSON result tree depends on the provided implementation of
'org.springframework.integration.support.json.JsonObjectMapper' (e.g. JsonNode for
Jackson). The default value is 'STRING'.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="jsonResultTypeEnumeration xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:simpleType name="jsonResultTypeEnumeration">
<xsd:restriction base="xsd:token">
<xsd:enumeration value="STRING" />
<xsd:enumeration value="NODE" />
</xsd:restriction>
</xsd:simpleType>
<xsd:element name="json-to-object-transformer">
<xsd:annotation>
<xsd:documentation>
@@ -2310,12 +2330,11 @@
This Jackson 1 ObjectMapper backward compatibility is deprecated
and will be removed in the Spring Integration 3.1 or above.
</xsd:documentation>
<!-- TODO: Revert in the Spring Integration 3.1 or above after removing Jackson 1 backward compatibility
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.support.json.JsonObjectMapper" />
</tool:annotation>
</xsd:appinfo>-->
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="id" type="xsd:string" />

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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,7 +48,7 @@ import org.springframework.messaging.Message;
*/
public abstract class AbstractJsonInboundMessageMapperTests {
private final JsonObjectMapper<?> mapper = JacksonJsonObjectMapperProvider.newInstance();
private final JsonObjectMapper<?, ?> mapper = JacksonJsonObjectMapperProvider.newInstance();
@Factory
public static Matcher<Message<?>> sameExceptImmutableHeaders(Message<?> operand) {

View File

@@ -10,12 +10,6 @@
<json-to-object-transformer id="defaultJacksonMapperTransformer" input-channel="defaultObjectMapperInput"
type="org.springframework.integration.json.TestPerson"/>
<json-to-object-transformer id="customJacksonMapperTransformer" input-channel="customObjectMapperInput"
type="org.springframework.integration.json.TestPerson"
object-mapper="customObjectMapper"/>
<beans:bean id="customObjectMapper" class="org.springframework.integration.json.JsonToObjectTransformerParserTests$CustomObjectMapper"/>
<json-to-object-transformer id="customJsonMapperTransformer" input-channel="customJsonObjectMapperInput"
type="org.springframework.integration.json.TestPerson"
object-mapper="customJsonObjectMapper"/>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -20,8 +20,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import org.codehaus.jackson.JsonParser.Feature;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -51,9 +49,6 @@ public class JsonToObjectTransformerParserTests {
@Autowired
private volatile MessageChannel defaultObjectMapperInput;
@Autowired
private volatile MessageChannel customObjectMapperInput;
@Autowired
private volatile MessageChannel customJsonObjectMapperInput;
@@ -61,19 +56,12 @@ public class JsonToObjectTransformerParserTests {
@Qualifier("defaultJacksonMapperTransformer.handler")
private MessageHandler defaultJacksonMapperTransformer;
@Autowired
@Qualifier("customJacksonMapperTransformer.handler")
private MessageHandler customJacksonMapperTransformer;
@Autowired
@Qualifier("customJsonMapperTransformer.handler")
private MessageHandler customJsonMapperTransformer;
@Autowired
private ObjectMapper customObjectMapper;
@Autowired
private JsonObjectMapper<?> jsonObjectMapper;
private JsonObjectMapper<?, ?> jsonObjectMapper;
@Test
public void defaultObjectMapper() {
@@ -95,27 +83,6 @@ public class JsonToObjectTransformerParserTests {
assertEquals("123 Main Street", person.getAddress().toString());
}
@Test
public void customObjectMapper() {
Object jsonToObjectTransformer = TestUtils.getPropertyValue(this.customJacksonMapperTransformer, "transformer");
JsonObjectMapper<?> jsonObjectMapper = TestUtils.getPropertyValue(jsonToObjectTransformer, "jsonObjectMapper", JsonObjectMapper.class);
assertSame(this.customObjectMapper, TestUtils.getPropertyValue(jsonObjectMapper, "objectMapper"));
String jsonString = "{firstName:'John', lastName:'Doe', age:42, address:{number:123, street:'Main Street'}}";
QueueChannel replyChannel = new QueueChannel();
Message<String> message = MessageBuilder.withPayload(jsonString).setReplyChannel(replyChannel).build();
this.customObjectMapperInput.send(message);
Message<?> reply = replyChannel.receive(0);
assertNotNull(reply);
assertNotNull(reply.getPayload());
assertEquals(TestPerson.class, reply.getPayload().getClass());
TestPerson person = (TestPerson) reply.getPayload();
assertEquals("John", person.getFirstName());
assertEquals("Doe", person.getLastName());
assertEquals(42, person.getAge());
assertEquals("123 Main Street", person.getAddress().toString());
}
@Test
public void testInt2831CustomJsonObjectMapper() {
Object jsonToObjectTransformer = TestUtils.getPropertyValue(this.customJsonMapperTransformer, "transformer");
@@ -133,15 +100,6 @@ public class JsonToObjectTransformerParserTests {
assertEquals(jsonString, result.getJson());
}
static class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
this.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, Boolean.TRUE);
this.configure(Feature.ALLOW_SINGLE_QUOTES, Boolean.TRUE);
}
}
@SuppressWarnings("rawtypes")
static class CustomJsonObjectMapper extends JsonObjectMapperAdapter {

View File

@@ -9,17 +9,15 @@
<object-to-json-transformer id="defaultTransformer" input-channel="defaultObjectMapperInput"/>
<object-to-json-transformer id="customTransformer" input-channel="customObjectMapperInput" object-mapper="customObjectMapper"/>
<object-to-json-transformer id="emptyContentTypeTransformer" input-channel="customObjectMapperInput" content-type=""/>
<object-to-json-transformer id="overridenContentTypeTransformer" input-channel="customObjectMapperInput" content-type="text/xml"/>
<beans:bean id="customObjectMapper" class="org.springframework.integration.json.ObjectToJsonTransformerParserTests$CustomObjectMapper"/>
<object-to-json-transformer id="customJsonObjectMapperTransformer" input-channel="customJsonObjectMapperInput"
object-mapper="customJsonObjectMapper"/>
<object-to-json-transformer id="jsonNodeTransformer" input-channel="jsonNodeInput" result-type="NODE"/>
<beans:bean id="customJsonObjectMapper" class="org.springframework.integration.json.ObjectToJsonTransformerParserTests$CustomJsonObjectMapper"/>
</beans:beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -19,29 +19,34 @@ package org.springframework.integration.json;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.codehaus.jackson.JsonGenerator.Feature;
import org.codehaus.jackson.map.ObjectMapper;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
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.JsonObjectMapperAdapter;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.fasterxml.jackson.databind.JsonNode;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
@@ -60,10 +65,10 @@ public class ObjectToJsonTransformerParserTests {
private volatile MessageChannel defaultObjectMapperInput;
@Autowired
private volatile MessageChannel customObjectMapperInput;
private volatile MessageChannel customJsonObjectMapperInput;
@Autowired
private volatile MessageChannel customJsonObjectMapperInput;
private volatile MessageChannel jsonNodeInput;
@Test
public void testContentType(){
@@ -77,10 +82,6 @@ public class ObjectToJsonTransformerParserTests {
assertTrue(transformed.getHeaders().containsKey(MessageHeaders.CONTENT_TYPE));
assertEquals("application/json", transformed.getHeaders().get(MessageHeaders.CONTENT_TYPE));
transformer =
TestUtils.getPropertyValue(context.getBean("customTransformer"), "handler.transformer", ObjectToJsonTransformer.class);
assertEquals("application/json", TestUtils.getPropertyValue(transformer, "contentType"));
transformer =
TestUtils.getPropertyValue(context.getBean("emptyContentTypeTransformer"), "handler.transformer", ObjectToJsonTransformer.class);
assertEquals("", TestUtils.getPropertyValue(transformer, "contentType"));
@@ -127,35 +128,6 @@ public class ObjectToJsonTransformerParserTests {
assertTrue(addressResult.contains("\"street\":\"Main Street\""));
}
@Test
public void customObjectMapper() {
TestAddress address = new TestAddress();
address.setNumber(123);
address.setStreet("Main Street");
TestPerson person = new TestPerson();
person.setFirstName("John");
person.setLastName("Doe");
person.setAge(42);
person.setAddress(address);
QueueChannel replyChannel = new QueueChannel();
Message<TestPerson> message = MessageBuilder.withPayload(person).setReplyChannel(replyChannel).build();
this.customObjectMapperInput.send(message);
Message<?> reply = replyChannel.receive(0);
assertNotNull(reply);
assertNotNull(reply.getPayload());
assertEquals(String.class, reply.getPayload().getClass());
String resultString = (String) reply.getPayload();
assertTrue(resultString.contains("firstName:\"John\""));
assertTrue(resultString.contains("lastName:\"Doe\""));
assertTrue(resultString.contains("age:42"));
Pattern addressPattern = Pattern.compile("(address:\\{.*?\\})");
Matcher matcher = addressPattern.matcher(resultString);
assertTrue(matcher.find());
String addressResult = matcher.group(1);
assertTrue(addressResult.contains("number:123"));
assertTrue(addressResult.contains("street:\"Main Street\""));
}
@Test
public void testInt2831CustomJsonObjectMapper() {
TestPerson person = new TestPerson();
@@ -173,15 +145,27 @@ public class ObjectToJsonTransformerParserTests {
assertEquals("{" + person.toString() + "}", resultString);
}
@Test
public void testNodeResultType() {
TestPerson person = new TestPerson();
person.setFirstName("John");
person.setLastName("Doe");
person.setAge(42);
QueueChannel replyChannel = new QueueChannel();
Message<TestPerson> message = MessageBuilder.withPayload(person).setReplyChannel(replyChannel).build();
this.jsonNodeInput.send(message);
Message<?> reply = replyChannel.receive(0);
assertNotNull(reply);
Object payload = reply.getPayload();
assertThat(payload, Matchers.instanceOf(JsonNode.class));
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
evaluationContext.addPropertyAccessor(new JsonPropertyAccessor());
Expression expression = new SpelExpressionParser().parseExpression("firstName.toString() == 'John' and age.toString() == '42'");
static class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
this.configure(Feature.QUOTE_FIELD_NAMES, Boolean.FALSE);
}
assertTrue(expression.getValue(evaluationContext, payload, Boolean.class));
}
static class CustomJsonObjectMapper extends JsonObjectMapperAdapter<Object> {
static class CustomJsonObjectMapper extends JsonObjectMapperAdapter<Object, Object> {
@Override
public String toJson(Object value) throws Exception {

View File

@@ -48,7 +48,7 @@ import org.springframework.util.Assert;
*/
public class MapJsonSerializer implements Serializer<Map<?, ?>>, Deserializer<Map<?, ?>> {
private volatile JsonObjectMapper<?> jsonObjectMapper = JacksonJsonObjectMapperProvider.newInstance();
private volatile JsonObjectMapper<?, ?> jsonObjectMapper = JacksonJsonObjectMapperProvider.newInstance();
private volatile Deserializer<byte[]> packetDeserializer = new ByteArrayLfSerializer();
@@ -59,7 +59,7 @@ public class MapJsonSerializer implements Serializer<Map<?, ?>>, Deserializer<Ma
* JSON. Use this if you wish to set additional {@link JsonObjectMapper} implementation features.
* @param jsonObjectMapper the jsonObjectMapper.
*/
public void setJsonObjectMapper(JsonObjectMapper<?> jsonObjectMapper) {
public void setJsonObjectMapper(JsonObjectMapper<?, ?> jsonObjectMapper) {
Assert.notNull(jsonObjectMapper, "'jsonObjectMapper' cannot be null");
this.jsonObjectMapper = jsonObjectMapper;
}

View File

@@ -361,13 +361,23 @@ public class Foo {
In addition to JSON Transformers, Spring Integration provides a built-in <emphasis>#jsonPath</emphasis>
SpEL function for use in expressions. For more information see <xref linkend="spel"/>.
</para>
<para>
<para id="transformer-xpath-spel-function">
<emphasis role="bold">#xpath SpEL Function</emphasis>
</para>
<para>
Since version <emphasis>3.0</emphasis>, Spring Integration also provides a built-in <emphasis>#xpath</emphasis>
SpEL function for use in expressions. For more information see <xref linkend="xpath-spel-function"/>.
</para>
<para>
Beginning with <emphasis>version 4.0</emphasis>, the <classname>ObjectToJsonTransformer</classname>
supports the <code>resultType</code> property, to specify the <emphasis>node</emphasis> JSON representation.
The result node tree representation depends on the implementation of the provided <interfacename>JsonObjectMapper</interfacename>.
By default, the <classname>ObjectToJsonTransformer</classname> uses a <classname>Jackson2JsonObjectMapper</classname>
and delegates the conversion of the object to the node tree to the <classname>ObjectMapper#valueToTree</classname> method.
The node JSON representation provides efficiency for using the <classname>JsonPropertyAccessor</classname>, when the
downstream message flow uses SpEL expressions with access to the properties of the JSON data. See
<xref linkend="spel-property-accessors"/>.
</para>
</section>
<section id="transformer-annotation">

View File

@@ -32,7 +32,7 @@
<ulink url="https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-3.0-to-4.0-Migration-Guide"
>Migration Guide</ulink>.
</para>
<section id="3.0-xpath-header-enricher-header-type">
<section id="4.0-xpath-header-enricher-header-type">
<title>Header Type for XPath Header Enricher</title>
<para>
The <code>header-type</code> attribute has been introduced for the <code>header</code> sub-element of the
@@ -41,5 +41,14 @@
For more information see <xref linkend="xml-xpath-header-enricher"/>.
</para>
</section>
<section id="4.0-object-to-json-transformer-result-type">
<title>Object To Json Transformer: Node Result</title>
<para>
The <code>result-type</code> attribute has been introduced for the <code>&lt;int:object-to-json-transformer&gt;</code>.
This attribute provides the target type for the result of object mapping to JSON.
It supports <code>STRING</code> (default) and <code>NODE</code>.
For more information see <xref linkend="transformer-xpath-spel-function"/>.
</para>
</section>
</section>
</chapter>