Merge pull request #774 from artembilan/INT-2831
* INT-2831: INT-2831: Add Jackson 2 support
This commit is contained in:
@@ -40,6 +40,7 @@ subprojects { subproject ->
|
||||
groovyVersion = '2.1.0'
|
||||
hamcrestVersion = '1.3'
|
||||
jacksonVersion = '1.9.2'
|
||||
jackson2Version = '2.1.2'
|
||||
javaxActivationVersion = '1.1.1'
|
||||
junitVersion = '4.11'
|
||||
log4jVersion = '1.2.12'
|
||||
@@ -145,7 +146,6 @@ project('spring-integration-amqp') {
|
||||
dependencies {
|
||||
compile project(":spring-integration-core")
|
||||
compile "org.springframework:spring-tx:$springVersion"
|
||||
compile("org.codehaus.jackson:jackson-mapper-asl:$jacksonVersion", optional)
|
||||
compile("org.springframework.amqp:spring-rabbit:$springAmqpVersion") {
|
||||
exclude group: 'org.springframework', module: 'spring-aop'
|
||||
exclude group: 'org.springframework', module: 'spring-beans'
|
||||
@@ -171,6 +171,7 @@ project('spring-integration-core') {
|
||||
compile "org.springframework:spring-tx:$springVersion"
|
||||
compile "org.springframework.retry:spring-retry:$springRetryVersion"
|
||||
compile("org.codehaus.jackson:jackson-mapper-asl:$jacksonVersion", optional)
|
||||
compile("com.fasterxml.jackson.core:jackson-databind:$jackson2Version", optional)
|
||||
testCompile "org.aspectj:aspectjweaver:$aspectjVersion"
|
||||
}
|
||||
}
|
||||
@@ -407,8 +408,6 @@ project('spring-integration-redis') {
|
||||
dependencies {
|
||||
compile project(":spring-integration-core")
|
||||
compile "org.springframework:spring-tx:$springVersion"
|
||||
compile("org.codehaus.jackson:jackson-mapper-asl:$jacksonVersion")
|
||||
compile("org.codehaus.jackson:jackson-core-asl:$jacksonVersion")
|
||||
compile ("org.springframework.data:spring-data-redis:$springDataRedisVersion") {
|
||||
exclude group: 'org.springframework', module: 'spring-core'
|
||||
exclude group: 'org.springframework', module: 'spring-context-support'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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,20 +16,23 @@
|
||||
|
||||
package org.springframework.integration.config.xml;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
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.JsonToObjectTransformer;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class JsonToObjectTransformerParser extends AbstractTransformerParser {
|
||||
|
||||
@Override
|
||||
protected String getTransformerClassName() {
|
||||
return "org.springframework.integration.json.JsonToObjectTransformer";
|
||||
return JsonToObjectTransformer.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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,22 +16,24 @@
|
||||
|
||||
package org.springframework.integration.config.xml;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.MessageHeaders;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ObjectToJsonTransformerParser extends AbstractTransformerParser {
|
||||
|
||||
@Override
|
||||
protected String getTransformerClassName() {
|
||||
return "org.springframework.integration.json.ObjectToJsonTransformer";
|
||||
return ObjectToJsonTransformer.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,8 +42,9 @@ public class ObjectToJsonTransformerParser extends AbstractTransformerParser {
|
||||
if (StringUtils.hasText(objectMapper)) {
|
||||
builder.addConstructorArgReference(objectMapper);
|
||||
}
|
||||
if (element.hasAttribute(MessageHeaders.CONTENT_TYPE)){
|
||||
if (element.hasAttribute("content-type")){
|
||||
builder.addPropertyValue("contentType", element.getAttribute("content-type"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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
|
||||
*
|
||||
* http://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.integration.json;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
|
||||
/**
|
||||
* Base {@link JsonInboundMessageMapper.JsonMessageParser} implementation for Jackson processors.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*
|
||||
* @see Jackson2JsonMessageParser
|
||||
* @see JacksonJsonMessageParser
|
||||
*/
|
||||
abstract class AbstractJacksonJsonMessageParser<P> implements JsonInboundMessageMapper.JsonMessageParser<P> {
|
||||
|
||||
protected final JsonObjectMapper<P> objectMapper;
|
||||
|
||||
protected volatile JsonInboundMessageMapper messageMapper;
|
||||
|
||||
protected AbstractJacksonJsonMessageParser(JsonObjectMapper<P> objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> doInParser(JsonInboundMessageMapper messageMapper, String jsonMessage) throws Exception {
|
||||
if (this.messageMapper == null) {
|
||||
this.messageMapper = messageMapper;
|
||||
}
|
||||
P parser = this.createJsonParser(jsonMessage);
|
||||
if (messageMapper.isMapToPayload()) {
|
||||
try {
|
||||
return MessageBuilder.withPayload(this.readPayload(parser)).build();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException("Mapping of JSON message " + jsonMessage +
|
||||
" directly to payload of type " + messageMapper.getPayloadType() + " failed.", ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return this.parseWithHeaders(parser, jsonMessage);
|
||||
}
|
||||
}
|
||||
|
||||
protected Object readPayload(P parser) throws Exception {
|
||||
return objectMapper.fromJson(parser, this.messageMapper.getPayloadType());
|
||||
}
|
||||
|
||||
protected abstract Message<?> parseWithHeaders(P parser, String jsonMessage) throws Exception;
|
||||
|
||||
protected abstract P createJsonParser(String jsonMessage) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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
|
||||
*
|
||||
* http://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.integration.json;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.integration.MessageHeaders;
|
||||
import org.springframework.integration.mapping.InboundMessageMapper;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract {@link InboundMessageMapper} implementation that maps incoming JSON messages
|
||||
* to a {@link org.springframework.integration.Message} with the specified payload type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*
|
||||
* @see JsonInboundMessageMapper
|
||||
*/
|
||||
public abstract class AbstractJsonInboundMessageMapper<P> implements InboundMessageMapper<String> {
|
||||
|
||||
protected static final String MESSAGE_FORMAT_ERROR = "JSON message is invalid. Expected a message in the format of either " +
|
||||
"{\"headers\":{...},\"payload\":{...}} or {\"payload\":{...}.\"headers\":{...}} but was ";
|
||||
|
||||
protected static final Map<String, Class<?>> DEFAULT_HEADER_TYPES = new HashMap<String, Class<?>>();
|
||||
|
||||
static {
|
||||
DEFAULT_HEADER_TYPES.put(MessageHeaders.PRIORITY, Integer.class);
|
||||
DEFAULT_HEADER_TYPES.put(MessageHeaders.EXPIRATION_DATE, Long.class);
|
||||
DEFAULT_HEADER_TYPES.put(MessageHeaders.SEQUENCE_SIZE, Integer.class);
|
||||
DEFAULT_HEADER_TYPES.put(MessageHeaders.SEQUENCE_NUMBER, Integer.class);
|
||||
}
|
||||
|
||||
protected final Type payloadType;
|
||||
|
||||
protected final Map<String, Class<?>> headerTypes = DEFAULT_HEADER_TYPES;
|
||||
|
||||
protected volatile boolean mapToPayload = false;
|
||||
|
||||
public AbstractJsonInboundMessageMapper(Type payloadType) {
|
||||
Assert.notNull(payloadType, "payloadType must not be null");
|
||||
this.payloadType = payloadType;
|
||||
}
|
||||
|
||||
public void setHeaderTypes(Map<String, Class<?>> headerTypes) {
|
||||
this.headerTypes.putAll(headerTypes);
|
||||
}
|
||||
|
||||
public void setMapToPayload(boolean mapToPayload) {
|
||||
this.mapToPayload = mapToPayload;
|
||||
}
|
||||
|
||||
protected abstract Object readPayload(P parser, String jsonMessage) throws Exception;
|
||||
|
||||
protected abstract Map<String, Object> readHeaders(P parser, String jsonMessage) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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
|
||||
*
|
||||
* http://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.integration.json;
|
||||
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link JsonInboundMessageMapper.JsonMessageParser} implementation that parses JSON messages
|
||||
* and builds a {@link Message} with the specified payload type from provided {@link JsonInboundMessageMapper}.
|
||||
* Uses Jackson 2 JSON-processor (@link https://github.com/FasterXML).
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
class Jackson2JsonMessageParser extends AbstractJacksonJsonMessageParser<JsonParser> {
|
||||
|
||||
public Jackson2JsonMessageParser() {
|
||||
super(new Jackson2JsonObjectMapper());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonParser createJsonParser(String jsonMessage) throws Exception {
|
||||
return new JsonFactory().createJsonParser(jsonMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Message<?> parseWithHeaders(JsonParser parser, String jsonMessage) throws Exception {
|
||||
String error = AbstractJsonInboundMessageMapper.MESSAGE_FORMAT_ERROR + jsonMessage;
|
||||
Assert.isTrue(parser.nextToken() == JsonToken.START_OBJECT, error);
|
||||
Map<String, Object> headers = null;
|
||||
Object payload = null;
|
||||
while (parser.nextToken() != JsonToken.END_OBJECT) {
|
||||
Assert.isTrue(parser.getCurrentToken() == JsonToken.FIELD_NAME, error);
|
||||
boolean isHeadersToken = "headers".equals(parser.getCurrentName());
|
||||
boolean isPayloadToken = "payload".equals(parser.getCurrentName());
|
||||
Assert.isTrue(isHeadersToken || isPayloadToken, error);
|
||||
if (isHeadersToken) {
|
||||
Assert.isTrue(parser.nextToken() == JsonToken.START_OBJECT, error);
|
||||
headers = readHeaders(parser, jsonMessage);
|
||||
}
|
||||
else if (isPayloadToken) {
|
||||
parser.nextToken();
|
||||
try {
|
||||
payload = this.readPayload(parser);
|
||||
}
|
||||
catch (JsonMappingException ex) {
|
||||
throw new IllegalArgumentException("Mapping payload of JSON message " + jsonMessage +
|
||||
" to payload type " + messageMapper.getPayloadType() + " failed.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
Assert.notNull(headers, error);
|
||||
return MessageBuilder.withPayload(payload).copyHeaders(headers).build();
|
||||
}
|
||||
|
||||
private Map<String, Object> readHeaders(JsonParser parser, String jsonMessage) throws Exception {
|
||||
Map<String, Object> headers = new LinkedHashMap<String, Object>();
|
||||
while (parser.nextToken() != JsonToken.END_OBJECT) {
|
||||
String headerName = parser.getCurrentName();
|
||||
parser.nextToken();
|
||||
Class<?> headerType = this.messageMapper.getHeaderTypes().containsKey(headerName) ?
|
||||
this.messageMapper.getHeaderTypes().get(headerName) : Object.class;
|
||||
try {
|
||||
headers.put(headerName, this.objectMapper.fromJson(parser, headerType));
|
||||
}
|
||||
catch (JsonMappingException ex) {
|
||||
throw new IllegalArgumentException("Mapping header \"" + headerName + "\" of JSON message " +
|
||||
jsonMessage + " to header type " + messageMapper.getPayloadType() + " failed.", ex);
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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
|
||||
*
|
||||
* http://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.integration.json;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
public class Jackson2JsonObjectMapper implements JsonObjectMapper<JsonParser> {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public Jackson2JsonObjectMapper() {
|
||||
this.objectMapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
public Jackson2JsonObjectMapper(ObjectMapper objectMapper) {
|
||||
Assert.notNull(objectMapper, "objectMapper must not be null");
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
public String toJson(Object value) throws Exception {
|
||||
return this.objectMapper.writeValueAsString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toJson(Object value, Writer writer) throws Exception {
|
||||
this.objectMapper.writeValue(writer, value);
|
||||
}
|
||||
|
||||
public <T> T fromJson(String json, Class<T> valueType) throws Exception {
|
||||
return this.objectMapper.readValue(json, valueType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T fromJson(Reader json, Class<T> valueType) throws Exception {
|
||||
return this.objectMapper.readValue(json, valueType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T fromJson(JsonParser parser, Type valueType) throws Exception {
|
||||
return this.objectMapper.readValue(parser, this.objectMapper.constructType(valueType));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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
|
||||
*
|
||||
* http://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.integration.json;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.codehaus.jackson.JsonFactory;
|
||||
import org.codehaus.jackson.JsonParser;
|
||||
import org.codehaus.jackson.JsonToken;
|
||||
import org.codehaus.jackson.map.JsonMappingException;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link JsonInboundMessageMapper.JsonMessageParser} implementation that parses JSON messages
|
||||
* and builds a {@link Message} with the specified payload type from provided {@link JsonInboundMessageMapper}.
|
||||
* Uses Jackson JSON-processor (@link http://jackson.codehaus.org).
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
class JacksonJsonMessageParser extends AbstractJacksonJsonMessageParser<JsonParser> {
|
||||
|
||||
JacksonJsonMessageParser() {
|
||||
super(new JacksonJsonObjectMapper());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonParser createJsonParser(String jsonMessage) throws Exception {
|
||||
return new JsonFactory().createJsonParser(jsonMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Message<?> parseWithHeaders(JsonParser parser, String jsonMessage) throws Exception {
|
||||
String error = AbstractJsonInboundMessageMapper.MESSAGE_FORMAT_ERROR + jsonMessage;
|
||||
Assert.isTrue(parser.nextToken() == JsonToken.START_OBJECT, error);
|
||||
Map<String, Object> headers = null;
|
||||
Object payload = null;
|
||||
while (parser.nextToken() != JsonToken.END_OBJECT) {
|
||||
Assert.isTrue(parser.getCurrentToken() == JsonToken.FIELD_NAME, error);
|
||||
boolean isHeadersToken = "headers".equals(parser.getCurrentName());
|
||||
boolean isPayloadToken = "payload".equals(parser.getCurrentName());
|
||||
Assert.isTrue(isHeadersToken || isPayloadToken, error);
|
||||
if (isHeadersToken) {
|
||||
Assert.isTrue(parser.nextToken() == JsonToken.START_OBJECT, error);
|
||||
headers = readHeaders(parser, jsonMessage);
|
||||
}
|
||||
else if (isPayloadToken) {
|
||||
parser.nextToken();
|
||||
try {
|
||||
payload = this.readPayload(parser);
|
||||
}
|
||||
catch (JsonMappingException ex) {
|
||||
throw new IllegalArgumentException("Mapping payload of JSON message " + jsonMessage +
|
||||
" to payload type " + messageMapper.getPayloadType() + " failed.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
Assert.notNull(headers, error);
|
||||
return MessageBuilder.withPayload(payload).copyHeaders(headers).build();
|
||||
}
|
||||
|
||||
private Map<String, Object> readHeaders(JsonParser parser, String jsonMessage) throws Exception {
|
||||
Map<String, Object> headers = new LinkedHashMap<String, Object>();
|
||||
while (parser.nextToken() != JsonToken.END_OBJECT) {
|
||||
String headerName = parser.getCurrentName();
|
||||
parser.nextToken();
|
||||
Class<?> headerType = this.messageMapper.getHeaderTypes().containsKey(headerName) ?
|
||||
this.messageMapper.getHeaderTypes().get(headerName) : Object.class;
|
||||
try {
|
||||
headers.put(headerName, this.objectMapper.fromJson(parser, headerType));
|
||||
}
|
||||
catch (JsonMappingException ex) {
|
||||
throw new IllegalArgumentException("Mapping header \"" + headerName + "\" of JSON message " +
|
||||
jsonMessage + " to header type " + messageMapper.getPayloadType() + " failed.", ex);
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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
|
||||
*
|
||||
* http://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.integration.json;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.codehaus.jackson.JsonParser;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Jackson JSON-processor (@link http://jackson.codehaus.org) {@linkplain JsonObjectMapper} implementation.
|
||||
* Delegates <code>toJson</code> and <code>fromJson</code>
|
||||
* to the {@linkplain org.codehaus.jackson.map.ObjectMapper}
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
public class JacksonJsonObjectMapper implements JsonObjectMapper<JsonParser> {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public JacksonJsonObjectMapper() {
|
||||
this.objectMapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
public JacksonJsonObjectMapper(ObjectMapper objectMapper) {
|
||||
Assert.notNull(objectMapper, "objectMapper must not be null");
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
public String toJson(Object value) throws Exception {
|
||||
return this.objectMapper.writeValueAsString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toJson(Object value, Writer writer) throws Exception {
|
||||
this.objectMapper.writeValue(writer, value);
|
||||
}
|
||||
|
||||
public <T> T fromJson(String json, Class<T> valueType) throws Exception {
|
||||
return this.objectMapper.readValue(json, valueType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T fromJson(Reader json, Class<T> valueType) throws Exception {
|
||||
return this.objectMapper.readValue(json, valueType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T fromJson(JsonParser parser, Type valueType) throws Exception {
|
||||
return this.objectMapper.readValue(parser, this.objectMapper.constructType(valueType));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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
|
||||
*
|
||||
* http://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.integration.json;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Simple factory to provide {@linkplain Jackson2JsonObjectMapper} or {@linkplain JacksonJsonObjectMapper}
|
||||
* instances dependently of jackson-databind or jackson-mapper-asl libs in the classpath.
|
||||
* If there are both libs in the classpath, it prefers Jackson 2 JSON-processor implementation.
|
||||
* If there is no any of them, {@linkplain IllegalStateException} will be thrown.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*
|
||||
* @see Jackson2JsonObjectMapper
|
||||
* @see JacksonJsonObjectMapper
|
||||
* @see JsonToObjectTransformer
|
||||
* @see ObjectToJsonTransformer
|
||||
*/
|
||||
public final class JacksonJsonObjectMapperProvider {
|
||||
|
||||
private static final ClassLoader classLoader = JacksonJsonObjectMapperProvider.class.getClassLoader();
|
||||
|
||||
private static final boolean jackson2Present =
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
|
||||
|
||||
private static final boolean jacksonPresent =
|
||||
ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", classLoader) &&
|
||||
ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", classLoader);
|
||||
|
||||
private static final IllegalStateException NO_JACKSON_LIB_EXCEPTION =
|
||||
new IllegalStateException("Neither jackson-databind.jar, nor jackson-mapper-asl.jar aren't presented in the classpath.");
|
||||
|
||||
public static JsonObjectMapper<?> newInstance() {
|
||||
if (jackson2Present) {
|
||||
return new Jackson2JsonObjectMapper();
|
||||
}
|
||||
if(jacksonPresent) {
|
||||
return new JacksonJsonObjectMapper();
|
||||
}
|
||||
throw NO_JACKSON_LIB_EXCEPTION;
|
||||
}
|
||||
|
||||
public static JsonInboundMessageMapper.JsonMessageParser<?> newJsonMessageParser() {
|
||||
if (jackson2Present) {
|
||||
return new Jackson2JsonMessageParser();
|
||||
}
|
||||
if(jacksonPresent) {
|
||||
return new Jackson2JsonMessageParser();
|
||||
}
|
||||
throw NO_JACKSON_LIB_EXCEPTION;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 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,142 +16,78 @@
|
||||
|
||||
package org.springframework.integration.json;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
|
||||
import org.codehaus.jackson.JsonFactory;
|
||||
import org.codehaus.jackson.JsonParser;
|
||||
import org.codehaus.jackson.JsonToken;
|
||||
import org.codehaus.jackson.map.JsonMappingException;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.map.type.TypeFactory;
|
||||
import org.codehaus.jackson.type.JavaType;
|
||||
import org.codehaus.jackson.type.TypeReference;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageHeaders;
|
||||
import org.springframework.integration.mapping.InboundMessageMapper;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link InboundMessageMapper} implementation that maps incoming JSON messages to a {@link Message} with the specified payload type.
|
||||
*
|
||||
* {@link org.springframework.integration.mapping.InboundMessageMapper} implementation that maps incoming JSON messages
|
||||
* to a {@link Message} with the specified payload type.
|
||||
* By default it uses {@link JacksonJsonObjectMapperProvider} to get Jackson or Jackson 2 {@link JsonMessageParser}
|
||||
* implementation dependently from classpath.
|
||||
* Any other {@link JsonMessageParser} implementation may be provided through the appropriate constructor.
|
||||
*
|
||||
* @author Jeremy Grelle
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class JsonInboundMessageMapper implements InboundMessageMapper<String> {
|
||||
|
||||
private static final String MESSAGE_FORMAT_ERROR = "JSON message is invalid. Expected a message in the format of either " +
|
||||
"{\"headers\":{...},\"payload\":{...}} or {\"payload\":{...}.\"headers\":{...}} but was ";
|
||||
|
||||
private static final Map<String, Class<?>> DEFAULT_HEADER_TYPES = new HashMap<String, Class<?>>();
|
||||
|
||||
static {
|
||||
DEFAULT_HEADER_TYPES.put(MessageHeaders.PRIORITY, Integer.class);
|
||||
DEFAULT_HEADER_TYPES.put(MessageHeaders.EXPIRATION_DATE, Long.class);
|
||||
DEFAULT_HEADER_TYPES.put(MessageHeaders.SEQUENCE_SIZE, Integer.class);
|
||||
DEFAULT_HEADER_TYPES.put(MessageHeaders.SEQUENCE_NUMBER, Integer.class);
|
||||
}
|
||||
|
||||
|
||||
private final JavaType payloadType;
|
||||
|
||||
private final Map<String, Class<?>> headerTypes = DEFAULT_HEADER_TYPES;
|
||||
|
||||
private volatile ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private volatile boolean mapToPayload = false;
|
||||
public class JsonInboundMessageMapper extends AbstractJsonInboundMessageMapper<JsonInboundMessageMapper.JsonMessageParser<?>> {
|
||||
|
||||
private volatile JsonMessageParser<?> messageParser;
|
||||
|
||||
public JsonInboundMessageMapper(Class<?> payloadType) {
|
||||
Assert.notNull(payloadType, "payloadType must not be null");
|
||||
this.payloadType = TypeFactory.defaultInstance().constructType(payloadType);
|
||||
this((Type) payloadType);
|
||||
}
|
||||
|
||||
public JsonInboundMessageMapper(TypeReference<?> typeReference) {
|
||||
Assert.notNull(typeReference, "typeReference must not be null");
|
||||
this.payloadType = TypeFactory.defaultInstance().constructType(typeReference);
|
||||
public JsonInboundMessageMapper(Type payloadType) {
|
||||
this(payloadType, null);
|
||||
}
|
||||
|
||||
|
||||
public void setObjectMapper(ObjectMapper objectMapper) {
|
||||
Assert.notNull(objectMapper, "objectMapper must not be null");
|
||||
this.objectMapper = objectMapper;
|
||||
public JsonInboundMessageMapper(Class<?> payloadType, JsonMessageParser<?> messageParser) {
|
||||
this((Type) payloadType, messageParser);
|
||||
}
|
||||
|
||||
public void setHeaderTypes(Map<String, Class<?>> headerTypes) {
|
||||
this.headerTypes.putAll(headerTypes);
|
||||
public JsonInboundMessageMapper(Type payloadType, JsonMessageParser<?> messageParser) {
|
||||
super(payloadType);
|
||||
this.messageParser = messageParser != null ? messageParser : JacksonJsonObjectMapperProvider.newJsonMessageParser();
|
||||
}
|
||||
|
||||
public void setMapToPayload(boolean mapToPayload) {
|
||||
this.mapToPayload = mapToPayload;
|
||||
public boolean isMapToPayload() {
|
||||
return mapToPayload;
|
||||
}
|
||||
|
||||
public Type getPayloadType() {
|
||||
return payloadType;
|
||||
}
|
||||
|
||||
public Map<String, Class<?>> getHeaderTypes() {
|
||||
return headerTypes;
|
||||
}
|
||||
|
||||
public Message<?> toMessage(String jsonMessage) throws Exception {
|
||||
JsonParser parser = new JsonFactory().createJsonParser(jsonMessage);
|
||||
if (this.mapToPayload) {
|
||||
try {
|
||||
return MessageBuilder.withPayload(readPayload(parser, jsonMessage)).build();
|
||||
}
|
||||
catch (JsonMappingException ex) {
|
||||
throw new IllegalArgumentException("Mapping of JSON message " + jsonMessage +
|
||||
" directly to payload of type " + this.payloadType.getRawClass().getName() + " failed.", ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
String error = MESSAGE_FORMAT_ERROR + jsonMessage;
|
||||
Assert.isTrue(parser.nextToken() == JsonToken.START_OBJECT, error);
|
||||
Map<String, Object> headers = null;
|
||||
Object payload = null;
|
||||
while(parser.nextToken() != JsonToken.END_OBJECT) {
|
||||
Assert.isTrue(parser.getCurrentToken() == JsonToken.FIELD_NAME, error);
|
||||
boolean isHeadersToken = "headers".equals(parser.getCurrentName());
|
||||
boolean isPayloadToken = "payload".equals(parser.getCurrentName());
|
||||
Assert.isTrue(isHeadersToken || isPayloadToken, error);
|
||||
if (isHeadersToken) {
|
||||
Assert.isTrue(parser.nextToken() == JsonToken.START_OBJECT, error);
|
||||
headers = readHeaders(parser, jsonMessage);
|
||||
}
|
||||
else if (isPayloadToken) {
|
||||
parser.nextToken();
|
||||
try {
|
||||
payload = readPayload(parser, jsonMessage);
|
||||
}
|
||||
catch (JsonMappingException ex) {
|
||||
throw new IllegalArgumentException("Mapping payload of JSON message " + jsonMessage +
|
||||
" to payload type " + this.payloadType.getRawClass().getName() + " failed.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
Assert.notNull(headers, error);
|
||||
return MessageBuilder.withPayload(payload).copyHeaders(headers).build();
|
||||
}
|
||||
return this.messageParser.doInParser(this, jsonMessage);
|
||||
}
|
||||
|
||||
protected Map<String, Object> readHeaders(JsonParser parser, String jsonMessage) throws Exception{
|
||||
Map<String, Object> headers = new LinkedHashMap<String, Object>();
|
||||
while (parser.nextToken() != JsonToken.END_OBJECT) {
|
||||
String headerName = parser.getCurrentName();
|
||||
parser.nextToken();
|
||||
Class<?> headerType = this.headerTypes.containsKey(headerName) ?
|
||||
this.headerTypes.get(headerName) : Object.class;
|
||||
try {
|
||||
headers.put(headerName, this.objectMapper.readValue(parser, headerType));
|
||||
}
|
||||
catch (JsonMappingException ex) {
|
||||
throw new IllegalArgumentException("Mapping header \"" + headerName + "\" of JSON message " +
|
||||
jsonMessage + " to header type " + this.payloadType.getRawClass().getName() + " failed.", ex);
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
@Override
|
||||
protected Map<String, Object> readHeaders(JsonMessageParser<?> parser, String jsonMessage) throws Exception {
|
||||
//No-op
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Object readPayload(JsonParser parser, String jsonMessage) throws Exception {
|
||||
return this.objectMapper.readValue(parser, this.payloadType);
|
||||
@Override
|
||||
protected Object readPayload(JsonMessageParser<?> parser, String jsonMessage) throws Exception {
|
||||
//No-op
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static interface JsonMessageParser<P> {
|
||||
|
||||
Message<?> doInParser(JsonInboundMessageMapper messageMapper, String jsonMessage) throws Exception;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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
|
||||
*
|
||||
* http://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.integration.json;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Strategy interface to convert an Object to/from the JSON representation.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*
|
||||
* @see JsonToObjectTransformer
|
||||
* @see ObjectToJsonTransformer
|
||||
* @see JacksonJsonObjectMapper
|
||||
* @see Jackson2JsonObjectMapper
|
||||
*/
|
||||
public interface JsonObjectMapper<P> {
|
||||
|
||||
String toJson(Object value) throws Exception;
|
||||
|
||||
void toJson(Object value, Writer writer) throws Exception;
|
||||
|
||||
<T> T fromJson(String json, Class<T> valueType) throws Exception;
|
||||
|
||||
<T> T fromJson(Reader json, Class<T> valueType) throws Exception;
|
||||
|
||||
<T> T fromJson(P parser, Type valueType) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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
|
||||
*
|
||||
* http://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.integration.json;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Simple {@linkplain JsonObjectMapper} adapter implementation, if there is no need
|
||||
* to provide entire operations implementation.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class JsonObjectMapperAdapter<P> implements JsonObjectMapper<P> {
|
||||
|
||||
@Override
|
||||
public String toJson(Object value) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toJson(Object value, Writer writer) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T fromJson(String json, Class<T> valueType) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T fromJson(Reader json, Class<T> valueType) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T fromJson(P parser, Type valueType) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -15,32 +15,31 @@
|
||||
*/
|
||||
|
||||
package org.springframework.integration.json;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.mapping.OutboundMessageMapper;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link OutboundMessageMapper} implementation the converts a {@link Message} to a JSON string representation.
|
||||
*
|
||||
*
|
||||
* @author Jeremy Grelle
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class JsonOutboundMessageMapper implements OutboundMessageMapper<String> {
|
||||
|
||||
private volatile boolean shouldExtractPayload = false;
|
||||
|
||||
private volatile ObjectMapper objectMapper = new ObjectMapper();
|
||||
private volatile JsonObjectMapper<?> jsonObjectMapper;
|
||||
|
||||
public JsonOutboundMessageMapper() {
|
||||
this(JacksonJsonObjectMapperProvider.newInstance());
|
||||
}
|
||||
|
||||
public void setObjectMapper(ObjectMapper objectMapper) {
|
||||
Assert.notNull(objectMapper, "objectMapper must not be null");
|
||||
this.objectMapper = objectMapper;
|
||||
public JsonOutboundMessageMapper(JsonObjectMapper<?> jsonObjectMapper) {
|
||||
Assert.notNull(jsonObjectMapper, "jsonObjectMapper must not be null");
|
||||
this.jsonObjectMapper = jsonObjectMapper;
|
||||
}
|
||||
|
||||
public void setShouldExtractPayload(boolean shouldExtractPayload) {
|
||||
@@ -48,14 +47,7 @@ public class JsonOutboundMessageMapper implements OutboundMessageMapper<String>
|
||||
}
|
||||
|
||||
public String fromMessage(Message<?> message) throws Exception {
|
||||
StringWriter writer = new StringWriter();
|
||||
if (this.shouldExtractPayload) {
|
||||
this.objectMapper.writeValue(writer, message.getPayload());
|
||||
}
|
||||
else {
|
||||
this.objectMapper.writeValue(writer, message);
|
||||
}
|
||||
return writer.toString();
|
||||
return this.jsonObjectMapper.toJson(this.shouldExtractPayload ? message.getPayload() : message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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,37 +16,66 @@
|
||||
|
||||
package org.springframework.integration.json;
|
||||
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
|
||||
import org.springframework.integration.transformer.AbstractPayloadTransformer;
|
||||
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.
|
||||
* By default this transformer uses {@linkplain JacksonJsonObjectMapperProvider} factory
|
||||
* to get an instance of Jackson 1 or Jackson 2 JSON-processor {@linkplain JsonObjectMapper} implementation
|
||||
* depending on the jackson-databind or jackson-mapper-asl libs on the classpath.
|
||||
* Any other {@linkplain JsonObjectMapper} implementation can be provided.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
* @see JsonObjectMapper
|
||||
* @see JacksonJsonObjectMapperProvider
|
||||
* @since 2.0
|
||||
*/
|
||||
public class JsonToObjectTransformer<T> extends AbstractPayloadTransformer<String, T> {
|
||||
|
||||
private final Class<T> targetClass;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private final JsonObjectMapper<?> jsonObjectMapper;
|
||||
|
||||
public JsonToObjectTransformer(Class<T> targetClass) {
|
||||
this(targetClass, null);
|
||||
}
|
||||
|
||||
public JsonToObjectTransformer(Class<T> targetClass, ObjectMapper objectMapper) {
|
||||
/**
|
||||
* Backward compatibility - allows existing configurations using Jackson 1.x to inject
|
||||
* an ObjectMapper directly.
|
||||
* @deprecated in favor of {@link #JsonToObjectTransformer(Class, JsonObjectMapper)}
|
||||
*/
|
||||
@Deprecated
|
||||
public JsonToObjectTransformer(Class<T> targetClass, Object objectMapper) throws ClassNotFoundException {
|
||||
Assert.notNull(targetClass, "targetClass must not be null");
|
||||
this.targetClass = targetClass;
|
||||
this.objectMapper = (objectMapper != null) ? objectMapper : new ObjectMapper();
|
||||
if (objectMapper != null) {
|
||||
try {
|
||||
Class<?> objectMapperClass = ClassUtils.forName("org.codehaus.jackson.map.ObjectMapper", ClassUtils.getDefaultClassLoader());
|
||||
Assert.isTrue(objectMapperClass.isAssignableFrom(objectMapper.getClass()));
|
||||
this.jsonObjectMapper = new JacksonJsonObjectMapper((org.codehaus.jackson.map.ObjectMapper) objectMapper);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.jsonObjectMapper = JacksonJsonObjectMapperProvider.newInstance();
|
||||
}
|
||||
}
|
||||
|
||||
public JsonToObjectTransformer(Class<T> targetClass, JsonObjectMapper<?> jsonObjectMapper) {
|
||||
Assert.notNull(targetClass, "targetClass must not be null");
|
||||
this.targetClass = targetClass;
|
||||
this.jsonObjectMapper = (jsonObjectMapper != null) ? jsonObjectMapper : JacksonJsonObjectMapperProvider.newInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T transformPayload(String payload) throws Exception {
|
||||
return this.objectMapper.readValue(payload, this.targetClass);
|
||||
return this.jsonObjectMapper.fromJson(payload, this.targetClass);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,42 +15,64 @@
|
||||
*/
|
||||
package org.springframework.integration.json;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageHeaders;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.transformer.AbstractTransformer;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Transformer implementation that converts a payload instance into a JSON string representation.
|
||||
* By default this transformer uses {@linkplain JacksonJsonObjectMapperProvider} factory
|
||||
* to get an instance of a Jackson or Jackson 2 JSON-processor {@linkplain JsonObjectMapper} implementation
|
||||
* depending on the jackson-databind or jackson-mapper-asl libs on the classpath.
|
||||
* Any other {@linkplain JsonObjectMapper} implementation can be provided.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author James Carr
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ObjectToJsonTransformer extends AbstractTransformer {
|
||||
|
||||
public static final String JSON_CONTENT_TYPE = "application/json";
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final JsonObjectMapper<?> jsonObjectMapper;
|
||||
|
||||
private volatile String contentType = JSON_CONTENT_TYPE;
|
||||
|
||||
private volatile boolean contentTypeExplicitlySet = false;
|
||||
|
||||
public ObjectToJsonTransformer(ObjectMapper objectMapper) {
|
||||
/**
|
||||
* Backward compatibility - allows existing configurations using Jackson 1.x to inject
|
||||
* an ObjectMapper directly.
|
||||
* @deprecated in favor of {@link #ObjectToJsonTransformer(JsonObjectMapper)}
|
||||
*/
|
||||
@Deprecated
|
||||
public ObjectToJsonTransformer(Object objectMapper) {
|
||||
Assert.notNull(objectMapper, "objectMapper must not be null");
|
||||
this.objectMapper = objectMapper;
|
||||
try {
|
||||
Class<?> objectMapperClass = ClassUtils.forName("org.codehaus.jackson.map.ObjectMapper", ClassUtils.getDefaultClassLoader());
|
||||
Assert.isTrue(objectMapperClass.isAssignableFrom(objectMapper.getClass()));
|
||||
this.jsonObjectMapper = new 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.objectMapper = new ObjectMapper();
|
||||
this.jsonObjectMapper = JacksonJsonObjectMapperProvider.newInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,29 +80,23 @@ public class ObjectToJsonTransformer extends AbstractTransformer {
|
||||
*
|
||||
* @param contentType
|
||||
*/
|
||||
public void setContentType(String contentType){
|
||||
public void setContentType(String contentType) {
|
||||
// only null assertion is needed since "" is a valid value
|
||||
Assert.notNull(contentType, "'contentType' must not be null");
|
||||
this.contentTypeExplicitlySet = true;
|
||||
this.contentType = contentType.trim();
|
||||
}
|
||||
|
||||
private String transformPayload(Object payload) throws Exception {
|
||||
StringWriter writer = new StringWriter();
|
||||
this.objectMapper.writeValue(writer, payload);
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doTransform(Message<?> message) throws Exception {
|
||||
String payload = this.transformPayload(message.getPayload());
|
||||
String payload = this.jsonObjectMapper.toJson(message.getPayload());
|
||||
MessageBuilder<String> messageBuilder = MessageBuilder.withPayload(payload);
|
||||
|
||||
LinkedCaseInsensitiveMap<Object> headers = new LinkedCaseInsensitiveMap<Object>();
|
||||
headers.putAll(message.getHeaders());
|
||||
|
||||
if (headers.containsKey(MessageHeaders.CONTENT_TYPE)) {
|
||||
if (this.contentTypeExplicitlySet){
|
||||
if (this.contentTypeExplicitlySet) {
|
||||
// override, unless empty
|
||||
if (StringUtils.hasLength(this.contentType)) {
|
||||
headers.put(MessageHeaders.CONTENT_TYPE, this.contentType);
|
||||
@@ -93,4 +109,5 @@ public class ObjectToJsonTransformer extends AbstractTransformer {
|
||||
messageBuilder.copyHeaders(headers);
|
||||
return messageBuilder.build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 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,7 +20,8 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.springframework.integration.json.JacksonJsonObjectMapperProvider;
|
||||
import org.springframework.integration.json.JsonObjectMapper;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -31,7 +32,7 @@ import org.springframework.util.StringUtils;
|
||||
* via the {@link ObjectToMapTransformer#setShouldFlattenKeys(boolean)} method. It supports Collections, Maps and Arrays
|
||||
* which means that for flat maps it will flatten an Object's properties. Below is an example showing how a flattened
|
||||
* Object hierarchy is represented when 'shouldFlattenKeys' is TRUE.<br>
|
||||
*
|
||||
*
|
||||
* <code>
|
||||
* public class Person {
|
||||
* public String name = "John";
|
||||
@@ -41,17 +42,20 @@ import org.springframework.util.StringUtils;
|
||||
* private String street = "123 Main Street";
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
*
|
||||
* The resulting Map would look like this:
|
||||
* <code>
|
||||
* {name=John, address.street=123 Main Street}
|
||||
* </code>
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ObjectToMapTransformer extends AbstractPayloadTransformer<Object, Map<?,?>> {
|
||||
|
||||
private final JsonObjectMapper<?> jsonObjectMapper = JacksonJsonObjectMapperProvider.newInstance();
|
||||
|
||||
private volatile boolean shouldFlattenKeys = true;
|
||||
|
||||
public void setShouldFlattenKeys(boolean shouldFlattenKeys) {
|
||||
@@ -60,8 +64,7 @@ public class ObjectToMapTransformer extends AbstractPayloadTransformer<Object, M
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Map<String, Object> transformPayload(Object payload) throws Exception {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
Map<String,Object> result = new ObjectMapper().readValue(mapper.writeValueAsString(payload), Map.class);
|
||||
Map<String,Object> result = this.jsonObjectMapper.fromJson(this.jsonObjectMapper.toJson(payload), Map.class);
|
||||
if (this.shouldFlattenKeys) {
|
||||
result = this.flattenMap(result);
|
||||
}
|
||||
@@ -99,9 +102,9 @@ public class ObjectToMapTransformer extends AbstractPayloadTransformer<Object, M
|
||||
}
|
||||
else if (element instanceof Collection) {
|
||||
this.doProcessCollection(propertyPrefix, (Collection<?>) element, resultMap);
|
||||
}
|
||||
}
|
||||
else if (element != null && element.getClass().isArray()) {
|
||||
Collection<?> collection = CollectionUtils.arrayToList(element);
|
||||
Collection<?> collection = CollectionUtils.arrayToList(element);
|
||||
this.doProcessCollection(propertyPrefix, collection, resultMap);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -2124,15 +2124,19 @@
|
||||
<xsd:attribute name="object-mapper" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Reference to a Jackson ObjectMapper instance to be provided optionally
|
||||
if the default ObjectMapper
|
||||
configuration is not desirable.
|
||||
Optional reference to a JsonObjectMapper instance.
|
||||
By default, a JsonObjectMapper that uses a Jackson 2 ObjectMapper, or Jackson ObjectMapper
|
||||
implementation is used, depending on the jars on the classpath.
|
||||
Note: for backward compatibility, this attribute can take a reference to the Jackson 1 ObjectMapper bean.
|
||||
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.codehaus.jackson.map.ObjectMapper" />
|
||||
<tool:expected-type type="org.springframework.integration.json.JsonObjectMapper" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:appinfo>-->
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
@@ -2166,15 +2170,19 @@
|
||||
<xsd:attribute name="object-mapper" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Reference to a Jackson ObjectMapper instance to be provided optionally
|
||||
if the default ObjectMapper
|
||||
configuration is not desirable.
|
||||
Optional reference to a JsonObjectMapper instance.
|
||||
By default, a JsonObjectMapper that uses a Jackson 2 ObjectMapper, or Jackson ObjectMapper
|
||||
implementation is used, depending on the jars on the classpath.
|
||||
Note: for backward compatibility this attribute can take a reference to the Jackson 1 ObjectMapper bean.
|
||||
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.codehaus.jackson.map.ObjectMapper" />
|
||||
<tool:expected-type type="org.springframework.integration.json.JsonObjectMapper" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:appinfo>-->
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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,18 +20,12 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.codehaus.jackson.JsonGenerationException;
|
||||
import org.codehaus.jackson.map.JsonMappingException;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.type.TypeReference;
|
||||
import org.hamcrest.Factory;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Test;
|
||||
@@ -43,11 +37,11 @@ import org.springframework.integration.support.MessageBuilder;
|
||||
* @author Jeremy Grelle
|
||||
* @author Mark Fisher
|
||||
* @author Dave Syer
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class JsonInboundMessageMapperTests {
|
||||
|
||||
private ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
private final JsonObjectMapper<?> mapper = JacksonJsonObjectMapperProvider.newInstance();
|
||||
|
||||
@Factory
|
||||
public static Matcher<Message<?>> sameExceptImmutableHeaders(Message<?> operand) {
|
||||
@@ -64,7 +58,7 @@ public class JsonInboundMessageMapperTests {
|
||||
Message<?> result = mapper.toMessage(jsonMessage);
|
||||
assertThat(result, sameExceptImmutableHeaders(expected));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testToMessageWithStringPayload() throws Exception {
|
||||
String jsonMessage = "\"myPayloadStuff\"";
|
||||
@@ -74,7 +68,7 @@ public class JsonInboundMessageMapperTests {
|
||||
Message<?> result = mapper.toMessage(jsonMessage);
|
||||
assertEquals(expected, result.getPayload());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testToMessageWithHeadersAndBeanPayload() throws Exception {
|
||||
TestBean bean = new TestBean();
|
||||
@@ -85,7 +79,7 @@ public class JsonInboundMessageMapperTests {
|
||||
Message<?> result = mapper.toMessage(jsonMessage);
|
||||
assertThat(result, sameExceptImmutableHeaders(expected));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testToMessageWithBeanPayload() throws Exception {
|
||||
TestBean expected = new TestBean();
|
||||
@@ -95,7 +89,7 @@ public class JsonInboundMessageMapperTests {
|
||||
Message<?> result = mapper.toMessage(jsonMessage);
|
||||
assertEquals(expected, result.getPayload());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testToMessageWithBeanHeaderAndStringPayload() throws Exception {
|
||||
TestBean bean = new TestBean();
|
||||
@@ -109,27 +103,27 @@ public class JsonInboundMessageMapperTests {
|
||||
Message<?> result = mapper.toMessage(jsonMessage);
|
||||
assertThat(result, sameExceptImmutableHeaders(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@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\"]}";
|
||||
List<String> expectedList = Arrays.asList(new String[]{"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 TypeReference<List<String>>(){});
|
||||
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(new ParameterizedTypeReference<List<String>>(){}.getType());
|
||||
Message<?> result = mapper.toMessage(jsonMessage);
|
||||
assertThat(result, sameExceptImmutableHeaders(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@Test
|
||||
public void testToMessageWithHeadersAndListOfBeansPayload() throws Exception {
|
||||
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) + "]}";
|
||||
List<TestBean> expectedList = Arrays.asList(new TestBean[]{bean1, 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 TypeReference<List<TestBean>>(){});
|
||||
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(new ParameterizedTypeReference<List<TestBean>>(){}.getType());
|
||||
Message<?> result = mapper.toMessage(jsonMessage);
|
||||
assertThat(result, sameExceptImmutableHeaders(expected));
|
||||
}
|
||||
@@ -236,10 +230,8 @@ public class JsonInboundMessageMapperTests {
|
||||
}
|
||||
|
||||
|
||||
private String getBeanAsJson(TestBean bean) throws JsonGenerationException, JsonMappingException, IOException {
|
||||
StringWriter writer = new StringWriter();
|
||||
mapper.writeValue(writer, bean);
|
||||
return writer.toString();
|
||||
};
|
||||
private String getBeanAsJson(TestBean bean) throws Exception {
|
||||
return mapper.toJson(bean);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,18 +2,24 @@
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
http://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
|
||||
<json-to-object-transformer input-channel="defaultObjectMapperInput"
|
||||
<json-to-object-transformer id="defaultJacksonMapperTransformer" input-channel="defaultObjectMapperInput"
|
||||
type="org.springframework.integration.json.JsonToObjectTransformerParserTests$TestPerson"/>
|
||||
|
||||
<json-to-object-transformer input-channel="customObjectMapperInput"
|
||||
<json-to-object-transformer id="customJacksonMapperTransformer" input-channel="customObjectMapperInput"
|
||||
type="org.springframework.integration.json.JsonToObjectTransformerParserTests$TestPerson"
|
||||
object-mapper="customObjectMapper"/>
|
||||
|
||||
<beans:bean id="customObjectMapper" class="org.springframework.integration.json.JsonToObjectTransformerParserTests$CustomObjectMapper"/>
|
||||
|
||||
</beans:beans>
|
||||
<json-to-object-transformer id="customJsonMapperTransformer" input-channel="customJsonObjectMapperInput"
|
||||
type="org.springframework.integration.json.JsonToObjectTransformerParserTests$TestPerson"
|
||||
object-mapper="customJsonObjectMapper"/>
|
||||
|
||||
<beans:bean id="customJsonObjectMapper" class="org.springframework.integration.json.JsonToObjectTransformerParserTests$CustomJsonObjectMapper"/>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -18,22 +18,26 @@ package org.springframework.integration.json;
|
||||
|
||||
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;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.core.MessageHandler;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@@ -46,9 +50,32 @@ public class JsonToObjectTransformerParserTests {
|
||||
@Autowired
|
||||
private volatile MessageChannel customObjectMapperInput;
|
||||
|
||||
@Autowired
|
||||
private volatile MessageChannel customJsonObjectMapperInput;
|
||||
|
||||
@Autowired
|
||||
@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;
|
||||
|
||||
@Test
|
||||
public void defaultObjectMapper() {
|
||||
Object jsonToObjectTransformer = TestUtils.getPropertyValue(this.defaultJacksonMapperTransformer, "transformer");
|
||||
assertEquals(Jackson2JsonObjectMapper.class, TestUtils.getPropertyValue(jsonToObjectTransformer, "jsonObjectMapper").getClass());
|
||||
|
||||
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();
|
||||
@@ -66,6 +93,10 @@ public class JsonToObjectTransformerParserTests {
|
||||
|
||||
@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();
|
||||
@@ -81,6 +112,23 @@ public class JsonToObjectTransformerParserTests {
|
||||
assertEquals("123 Main Street", person.getAddress().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt2831CustomJsonObjectMapper() {
|
||||
Object jsonToObjectTransformer = TestUtils.getPropertyValue(this.customJsonMapperTransformer, "transformer");
|
||||
assertSame(this.jsonObjectMapper, TestUtils.getPropertyValue(jsonToObjectTransformer, "jsonObjectMapper", JsonObjectMapper.class));
|
||||
|
||||
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.customJsonObjectMapperInput.send(message);
|
||||
Message<?> reply = replyChannel.receive(0);
|
||||
assertNotNull(reply);
|
||||
assertNotNull(reply.getPayload());
|
||||
assertEquals(TestJsonContainer.class, reply.getPayload().getClass());
|
||||
TestJsonContainer result = (TestJsonContainer) reply.getPayload();
|
||||
assertEquals(jsonString, result.getJson());
|
||||
}
|
||||
|
||||
|
||||
static class TestPerson {
|
||||
|
||||
@@ -163,4 +211,26 @@ public class JsonToObjectTransformerParserTests {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static class CustomJsonObjectMapper extends JsonObjectMapperAdapter {
|
||||
|
||||
@Override
|
||||
public Object fromJson(String json, Class valueType) throws Exception {
|
||||
return new TestJsonContainer(json);
|
||||
}
|
||||
}
|
||||
|
||||
static class TestJsonContainer {
|
||||
|
||||
private final String json;
|
||||
|
||||
TestJsonContainer(String json) {
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
public String getJson() {
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -24,6 +24,7 @@ import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class JsonToObjectTransformerTests {
|
||||
@@ -44,7 +45,8 @@ public class JsonToObjectTransformerTests {
|
||||
ObjectMapper customMapper = new ObjectMapper();
|
||||
customMapper.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, Boolean.TRUE);
|
||||
customMapper.configure(Feature.ALLOW_SINGLE_QUOTES, Boolean.TRUE);
|
||||
JsonToObjectTransformer<TestPerson> transformer = new JsonToObjectTransformer<TestPerson>(TestPerson.class, customMapper);
|
||||
JsonToObjectTransformer<TestPerson> transformer =
|
||||
new JsonToObjectTransformer<TestPerson>(TestPerson.class, new JacksonJsonObjectMapper(customMapper));
|
||||
String jsonString = "{firstName:'John', lastName:'Doe', age:42, address:{number:123, street:'Main Street'}}";
|
||||
TestPerson person = transformer.transformPayload(jsonString);
|
||||
assertEquals("John", person.getFirstName());
|
||||
@@ -53,6 +55,12 @@ public class JsonToObjectTransformerTests {
|
||||
assertEquals("123 Main Street", person.getAddress().toString());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testInt2831IllegalArgument() throws Exception {
|
||||
new JsonToObjectTransformer<String>(String.class, new Object());
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestPerson {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
http://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
@@ -17,4 +17,9 @@
|
||||
|
||||
<beans:bean id="customObjectMapper" class="org.springframework.integration.json.ObjectToJsonTransformerParserTests$CustomObjectMapper"/>
|
||||
|
||||
</beans:beans>
|
||||
<object-to-json-transformer id="customJsonObjectMapperTransformer" input-channel="customJsonObjectMapperInput"
|
||||
object-mapper="customJsonObjectMapper"/>
|
||||
|
||||
<beans:bean id="customJsonObjectMapper" class="org.springframework.integration.json.ObjectToJsonTransformerParserTests$CustomJsonObjectMapper"/>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@@ -58,12 +59,17 @@ public class ObjectToJsonTransformerParserTests {
|
||||
@Autowired
|
||||
private volatile MessageChannel customObjectMapperInput;
|
||||
|
||||
@Autowired
|
||||
private volatile MessageChannel customJsonObjectMapperInput;
|
||||
|
||||
@Test
|
||||
public void testContentType(){
|
||||
ObjectToJsonTransformer transformer =
|
||||
TestUtils.getPropertyValue(context.getBean("defaultTransformer"), "handler.transformer", ObjectToJsonTransformer.class);
|
||||
assertEquals("application/json", TestUtils.getPropertyValue(transformer, "contentType"));
|
||||
|
||||
assertEquals(Jackson2JsonObjectMapper.class, TestUtils.getPropertyValue(transformer, "jsonObjectMapper").getClass());
|
||||
|
||||
Message<?> transformed = transformer.transform(MessageBuilder.withPayload("foo").build());
|
||||
assertTrue(transformed.getHeaders().containsKey(MessageHeaders.CONTENT_TYPE));
|
||||
assertEquals("application/json", transformed.getHeaders().get(MessageHeaders.CONTENT_TYPE));
|
||||
@@ -147,6 +153,23 @@ public class ObjectToJsonTransformerParserTests {
|
||||
assertTrue(addressResult.contains("street:\"Main Street\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt2831CustomJsonObjectMapper() {
|
||||
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.customJsonObjectMapperInput.send(message);
|
||||
Message<?> reply = replyChannel.receive(0);
|
||||
assertNotNull(reply);
|
||||
assertNotNull(reply.getPayload());
|
||||
assertEquals(String.class, reply.getPayload().getClass());
|
||||
String resultString = (String) reply.getPayload();
|
||||
assertEquals("{" + person.toString() + "}", resultString);
|
||||
}
|
||||
|
||||
|
||||
static class TestPerson {
|
||||
|
||||
@@ -193,8 +216,8 @@ public class ObjectToJsonTransformerParserTests {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "name=" + this.firstName + " " + this.lastName
|
||||
+ ", age=" + this.age + ", address=" + this.address;
|
||||
return "\"name\":\"" + this.firstName + " " + this.lastName
|
||||
+ "\", \"age\":" + this.age + ", \"address\":\"" + this.address + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,4 +259,12 @@ public class ObjectToJsonTransformerParserTests {
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomJsonObjectMapper extends JsonObjectMapperAdapter<Object> {
|
||||
|
||||
@Override
|
||||
public String toJson(Object value) throws Exception {
|
||||
return "{" + value.toString() + "}";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.integration.support.MessageBuilder;
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ObjectToJsonTransformerTests {
|
||||
@@ -123,7 +124,7 @@ public class ObjectToJsonTransformerTests {
|
||||
public void objectPayloadWithCustomObjectMapper() throws Exception {
|
||||
ObjectMapper customMapper = new ObjectMapper();
|
||||
customMapper.configure(Feature.QUOTE_FIELD_NAMES, Boolean.FALSE);
|
||||
ObjectToJsonTransformer transformer = new ObjectToJsonTransformer(customMapper);
|
||||
ObjectToJsonTransformer transformer = new ObjectToJsonTransformer(new JacksonJsonObjectMapper(customMapper));
|
||||
TestPerson person = new TestPerson("John", "Doe", 42);
|
||||
person.setAddress(new TestAddress(123, "Main Street"));
|
||||
String result = (String) transformer.transform(new GenericMessage<TestPerson>(person)).getPayload();
|
||||
@@ -138,15 +139,20 @@ public class ObjectToJsonTransformerTests {
|
||||
assertTrue(addressResult.contains("street:\"Main Street\""));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testInt2831IllegalArgument() throws Exception {
|
||||
new ObjectToJsonTransformer(new Object());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestPerson {
|
||||
|
||||
private String firstName;
|
||||
private final String firstName;
|
||||
|
||||
private String lastName;
|
||||
private final String lastName;
|
||||
|
||||
private int age;
|
||||
private final int age;
|
||||
|
||||
private TestAddress address;
|
||||
|
||||
@@ -182,9 +188,9 @@ public class ObjectToJsonTransformerTests {
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestAddress {
|
||||
|
||||
private int number;
|
||||
private final int number;
|
||||
|
||||
private String street;
|
||||
private final String street;
|
||||
|
||||
|
||||
public TestAddress(int number, String street) {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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
|
||||
*
|
||||
* http://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.integration.json;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Copy of {@link org.springframework.core.ParameterizedTypeReference} from Spring Framework 3.2
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
//TODO Remove it after upgrade to Spring Framework 3.2 in favor to use org.springframework.core.ParameterizedTypeReference
|
||||
public abstract class ParameterizedTypeReference<T> {
|
||||
|
||||
private final Type type;
|
||||
|
||||
protected ParameterizedTypeReference() {
|
||||
Class<?> parameterizedTypeReferenceSubClass = findParameterizedTypeReferenceSubClass(getClass());
|
||||
|
||||
Type type = parameterizedTypeReferenceSubClass.getGenericSuperclass();
|
||||
Assert.isInstanceOf(ParameterizedType.class, type);
|
||||
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
Assert.isTrue(parameterizedType.getActualTypeArguments().length == 1);
|
||||
|
||||
this.type = parameterizedType.getActualTypeArguments()[0];
|
||||
}
|
||||
|
||||
private static Class<?> findParameterizedTypeReferenceSubClass(Class<?> child) {
|
||||
|
||||
Class<?> parent = child.getSuperclass();
|
||||
|
||||
if (Object.class.equals(parent)) {
|
||||
throw new IllegalStateException("Expected ParameterizedTypeReference superclass");
|
||||
}
|
||||
else if (ParameterizedTypeReference.class.equals(parent)) {
|
||||
return child;
|
||||
}
|
||||
else {
|
||||
return findParameterizedTypeReferenceSubClass(parent);
|
||||
}
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof ParameterizedTypeReference) {
|
||||
ParameterizedTypeReference<?> other = (ParameterizedTypeReference<?>) o;
|
||||
return this.type.equals(other.type);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.type.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ParameterizedTypeReference<" + this.type + ">";
|
||||
}
|
||||
}
|
||||
@@ -225,17 +225,31 @@ public class Kid {
|
||||
<programlisting language="xml"><![CDATA[<int:json-to-object-transformer input-channel="objectMapperInput"
|
||||
type="foo.MyDomainObject" object-mapper="customObjectMapper"/>]]></programlisting>
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
Beginning with version 3.0, the <code>object-mapper</code> attribute references an instance of a new
|
||||
strategy interface <interfacename>JsonObjectMapper</interfacename>. This abstraction allows multiple
|
||||
implementations of json mappers to be used. Implementations that wrap Jackson 1.x and Jackson 2 are
|
||||
provided, with the version being detected on the classpath. These classes are
|
||||
<classname>JacksonJsonObjectMapper</classname> and <classname>Jackson2JsonObjectMapper</classname>.
|
||||
</para>
|
||||
<para>
|
||||
For backward compatibility, a simple Jackson 1.x <classname>ObjectMapper</classname> can be provided
|
||||
instead of a <interfacename>JsonObjectMapper</interfacename>. This will be removed in a future release.
|
||||
</para>
|
||||
</note>
|
||||
<para>
|
||||
You may wish to consider using a FactoryBean or simple factory method to create the ObjectMapper with
|
||||
You may wish to consider using a <interfacename>FactoryBean</interfacename> or simple factory
|
||||
method to create the <classname>JsonObjectMapper</classname> with
|
||||
the required characteristics.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="java"><![CDATA[public class ObjectMapperFactory {
|
||||
|
||||
public static ObjectMapper getMapper() {
|
||||
public static Jackson2JsonObjectMapper getMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
|
||||
return mapper;
|
||||
return new Jackson2JsonObjectMapper(mapper);
|
||||
}
|
||||
}]]></programlisting>
|
||||
</para>
|
||||
|
||||
@@ -127,5 +127,14 @@
|
||||
please see <xref linkend="jdbc-message-store-generic"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Jackson Support (JSON)</title>
|
||||
<para>
|
||||
A new abstraction for JSON conversion has been introduced. Implementations for Jackson 1.x
|
||||
and Jackson 2 are currently provided, with the version being determined by presence on
|
||||
the classpath. Previously, only Jackson 1.x was supported. For more information,
|
||||
see 'JSON Transformers' in <xref linkend="transformer"/>.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
Reference in New Issue
Block a user