INT-4316 Add ObjectToMapTransformer.mapper option
JIRA: https://jira.spring.io/browse/INT-4316 To allow to customize a JSON representation of the Object, provide a `JsonObjectMapper`-based ctor for the `ObjectToMapTransformer`
This commit is contained in:
@@ -316,6 +316,7 @@ project('spring-integration-core') {
|
||||
|
||||
testCompile ("org.aspectj:aspectjweaver:$aspectjVersion")
|
||||
testCompile "io.projectreactor:reactor-test:$reactorVersion"
|
||||
testCompile ("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson2Version")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -21,10 +21,13 @@ import org.w3c.dom.Element;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.transformer.ObjectToMapTransformer;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mauro Franceschini
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ObjectToMapTransformerParser extends AbstractTransformerParser {
|
||||
@@ -36,6 +39,11 @@ public class ObjectToMapTransformerParser extends AbstractTransformerParser {
|
||||
|
||||
@Override
|
||||
protected void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
String objectMapper = element.getAttribute("object-mapper");
|
||||
if (StringUtils.hasText(objectMapper)) {
|
||||
builder.addConstructorArgReference(objectMapper);
|
||||
}
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "flatten", "shouldFlattenKeys");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-2017 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.
|
||||
@@ -71,6 +71,16 @@ public abstract class Transformers {
|
||||
return transformer;
|
||||
}
|
||||
|
||||
public static ObjectToMapTransformer toMap(JsonObjectMapper<?, ?> jsonObjectMapper) {
|
||||
return new ObjectToMapTransformer(jsonObjectMapper);
|
||||
}
|
||||
|
||||
public static ObjectToMapTransformer toMap(JsonObjectMapper<?, ?> jsonObjectMapper, boolean shouldFlattenKeys) {
|
||||
ObjectToMapTransformer transformer = new ObjectToMapTransformer();
|
||||
transformer.setShouldFlattenKeys(shouldFlattenKeys);
|
||||
return transformer;
|
||||
}
|
||||
|
||||
public static MapToObjectTransformer fromMap(Class<?> targetClass) {
|
||||
return new MapToObjectTransformer(targetClass);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2017 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.
|
||||
@@ -44,9 +44,12 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
* <ul>
|
||||
* <li>{@link MapperFeature#DEFAULT_VIEW_INCLUSION} is disabled</li>
|
||||
* <li>{@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} is disabled</li>
|
||||
* <li>{@link ObjectMapper#findAndRegisterModules()} is performed</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @author Vikas Prasad
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper<JsonNode, JsonParser, JavaType> {
|
||||
@@ -57,6 +60,7 @@ public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper<Js
|
||||
this.objectMapper = new ObjectMapper();
|
||||
this.objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
|
||||
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
this.objectMapper.findAndRegisterModules();
|
||||
}
|
||||
|
||||
public Jackson2JsonObjectMapper(ObjectMapper objectMapper) {
|
||||
|
||||
@@ -27,10 +27,11 @@ import org.springframework.util.ClassUtils;
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
* @author Vikas Prasad
|
||||
* @since 3.0
|
||||
*
|
||||
* @see Jackson2JsonObjectMapper
|
||||
* @see org.springframework.integration.support.json.BoonJsonObjectMapper
|
||||
* @see BoonJsonObjectMapper
|
||||
*/
|
||||
public final class JsonObjectMapperProvider {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.Entry;
|
||||
|
||||
import org.springframework.integration.support.json.JsonObjectMapper;
|
||||
import org.springframework.integration.support.json.JsonObjectMapperProvider;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Will transform an object graph into a Map. It supports a conventional Map (map of maps) where complex attributes are
|
||||
* represented as Map values as well as a flat Map where keys document the path to the value. By default it will
|
||||
* transform to a flat Map. If you need to transform to a Map of Maps set the 'shouldFlattenKeys' property to 'false'
|
||||
* 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>
|
||||
* Transforms an object graph into a Map. It supports a conventional Map (map of maps)
|
||||
* where complex attributes are represented as Map values as well as a flat Map
|
||||
* where keys document the path to the value. By default it will transform to a flat Map.
|
||||
* If you need to transform to a Map of Maps set the 'shouldFlattenKeys' property to 'false'
|
||||
* 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.
|
||||
*<p>
|
||||
* The transformation is based on to and then from JSON conversion.
|
||||
*
|
||||
* <code>
|
||||
* public class Person {
|
||||
@@ -52,14 +57,36 @@ import org.springframework.util.StringUtils;
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
* @author Vikas Prasad
|
||||
*
|
||||
* @since 2.0
|
||||
*
|
||||
* @see JsonObjectMapperProvider
|
||||
*/
|
||||
public class ObjectToMapTransformer extends AbstractPayloadTransformer<Object, Map<?, ?>> {
|
||||
|
||||
private final JsonObjectMapper<?, ?> jsonObjectMapper = JsonObjectMapperProvider.newInstance();
|
||||
private final JsonObjectMapper<?, ?> jsonObjectMapper;
|
||||
|
||||
private volatile boolean shouldFlattenKeys = true;
|
||||
|
||||
/**
|
||||
* Construct with the default {@link JsonObjectMapper} instance available via
|
||||
* {@link JsonObjectMapperProvider#newInstance() factory}.
|
||||
*/
|
||||
public ObjectToMapTransformer() {
|
||||
this(JsonObjectMapperProvider.newInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct with the provided {@link JsonObjectMapper} instance.
|
||||
* @param jsonObjectMapper the {@link JsonObjectMapper} to use.
|
||||
* @since 5.0
|
||||
*/
|
||||
public ObjectToMapTransformer(JsonObjectMapper<?, ?> jsonObjectMapper) {
|
||||
Assert.notNull(jsonObjectMapper, "'jsonObjectMapper' must not be null");
|
||||
this.jsonObjectMapper = jsonObjectMapper;
|
||||
}
|
||||
|
||||
public void setShouldFlattenKeys(boolean shouldFlattenKeys) {
|
||||
this.shouldFlattenKeys = shouldFlattenKeys;
|
||||
}
|
||||
@@ -88,7 +115,7 @@ public class ObjectToMapTransformer extends AbstractPayloadTransformer<Object, M
|
||||
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 {
|
||||
@@ -97,7 +124,7 @@ public class ObjectToMapTransformer extends AbstractPayloadTransformer<Object, M
|
||||
}
|
||||
|
||||
private Map<String, Object> flattenMap(Map<String, Object> result) {
|
||||
Map<String, Object> resultMap = new HashMap<String, Object>();
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
this.doFlatten("", result, resultMap);
|
||||
return resultMap;
|
||||
}
|
||||
@@ -111,7 +138,7 @@ public class ObjectToMapTransformer extends AbstractPayloadTransformer<Object, M
|
||||
}
|
||||
}
|
||||
|
||||
private void doProcessCollection(String propertyPrefix, Collection<?> list, Map<String, Object> resultMap) {
|
||||
private void doProcessCollection(String propertyPrefix, Collection<?> list, Map<String, Object> resultMap) {
|
||||
int counter = 0;
|
||||
for (Object element : list) {
|
||||
this.doProcessElement(propertyPrefix + "[" + counter + "]", element, resultMap);
|
||||
|
||||
@@ -1832,7 +1832,7 @@
|
||||
<xsd:element name="payload-deserializing-transformer"
|
||||
type="payload-deserializing-transformer-type"/>
|
||||
<xsd:element name="object-to-string-transformer" type="specialized-transformer-charset-aware-type"/>
|
||||
<xsd:element name="object-to-map-transformer" type="specialized-transformer-type"/>
|
||||
<xsd:element name="object-to-map-transformer" type="object-to-map-transformer-type"/>
|
||||
<xsd:element name="map-to-object-transformer" type="map-to-object-transformer-type"/>
|
||||
<xsd:element name="object-to-json-transformer" type="object-to-json-transformer-type"/>
|
||||
<xsd:element name="json-to-object-transformer" type="json-to-object-transformer-type"/>
|
||||
@@ -2441,22 +2441,42 @@
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="specialized-transformer-type">
|
||||
<xsd:extension base="object-to-map-transformer-type">
|
||||
<xsd:attributeGroup ref="inputOutputChannelGroup" />
|
||||
<xsd:attribute name="flatten" type="xsd:string" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specifies if the result Map of Maps should be transformed further to flat keys of
|
||||
object's property paths.
|
||||
Default is 'true'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="object-to-map-transformer-type">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="specialized-transformer-type">
|
||||
<xsd:attribute name="flatten" type="xsd:string" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specifies if the result Map of Maps should be transformed further to flat keys of
|
||||
object's property paths.
|
||||
Default is 'true'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="object-mapper">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Optional reference to a JsonObjectMapper instance.
|
||||
By default, a JsonObjectMapper that uses a JsonObjectMapperProvider.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.support.json.JsonObjectMapper" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:element name="map-to-object-transformer">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
@@ -118,6 +118,10 @@
|
||||
<logging-channel-adapter id="loggingChannelAdapterWithinChain" level="WARN"/>
|
||||
</chain>
|
||||
|
||||
<beans:bean id="jsonObjectMapper" class="org.mockito.Mockito" factory-method="mock">
|
||||
<beans:constructor-arg value="org.springframework.integration.support.json.JsonObjectMapper"/>
|
||||
</beans:bean>
|
||||
|
||||
<chain id="subComponentsIdSupport1" input-channel="subComponentsIdSupport1Channel">
|
||||
<splitter id="splitterWithinChain"/>
|
||||
<resequencer id="resequencerWithinChain"/>
|
||||
@@ -130,7 +134,9 @@
|
||||
<gateway id="gatewayWithinChain" request-channel="strings" reply-channel="numbers"
|
||||
request-timeout="1000" reply-timeout="100"/>
|
||||
<object-to-string-transformer id="objectToStringTransformerWithinChain"/>
|
||||
<object-to-map-transformer id="objectToMapTransformerWithinChain"/>
|
||||
<object-to-map-transformer id="objectToMapTransformerWithinChain"
|
||||
flatten="false"
|
||||
object-mapper="jsonObjectMapper"/>
|
||||
<map-to-object-transformer id="mapToObjectTransformerWithinChain"
|
||||
type="org.springframework.integration.config.ChainParserTests$FooPojo"/>
|
||||
<object-to-json-transformer id="objectToJsonTransformerWithinChain"/>
|
||||
@@ -147,7 +153,7 @@
|
||||
</chain>
|
||||
|
||||
<chain id="recipientListRouterChain" input-channel="subComponentsIdSupport3Channel">
|
||||
<recipient-list-router id="recipientListRouterWithinChain">
|
||||
<recipient-list-router id="recipientListRouterWithinChain">
|
||||
<recipient channel="strings"/>
|
||||
<recipient channel="numbers"/>
|
||||
</recipient-list-router>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
@@ -59,8 +60,10 @@ import org.springframework.integration.handler.ReplyRequiredException;
|
||||
import org.springframework.integration.handler.ServiceActivatingHandler;
|
||||
import org.springframework.integration.message.MessageMatcher;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.support.json.JsonObjectMapper;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.transformer.MessageTransformingHandler;
|
||||
import org.springframework.integration.transformer.ObjectToMapTransformer;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
@@ -129,7 +132,8 @@ public class ChainParserTests {
|
||||
@Autowired
|
||||
private MessageChannel loggingChannelAdapterChannel;
|
||||
|
||||
@Autowired @Qualifier("logChain.handler")
|
||||
@Autowired
|
||||
@Qualifier("logChain.handler")
|
||||
private MessageHandlerChain logChain;
|
||||
|
||||
@Autowired
|
||||
@@ -321,7 +325,7 @@ public class ChainParserTests {
|
||||
DirectFieldAccessor dfa = new DirectFieldAccessor(handler);
|
||||
dfa.setPropertyValue("messageLogger", logger);
|
||||
|
||||
this.loggingChannelAdapterChannel.send(MessageBuilder.withPayload(new byte[] {116, 101, 115, 116}).build());
|
||||
this.loggingChannelAdapterChannel.send(MessageBuilder.withPayload(new byte[] { 116, 101, 115, 116 }).build());
|
||||
assertNotNull(log.get());
|
||||
assertEquals("TEST", log.get());
|
||||
}
|
||||
@@ -392,13 +396,23 @@ public class ChainParserTests {
|
||||
GatewayProxyFactoryBean.class);
|
||||
assertEquals("strings", TestUtils.getPropertyValue(gatewayProxyFactoryBean, "defaultRequestChannelName"));
|
||||
assertEquals("numbers", TestUtils.getPropertyValue(gatewayProxyFactoryBean, "defaultReplyChannelName"));
|
||||
assertEquals(new Long(1000), TestUtils
|
||||
assertEquals(1000L, TestUtils
|
||||
.getPropertyValue(gatewayProxyFactoryBean, "defaultRequestTimeout", Expression.class).getValue());
|
||||
assertEquals(new Long(100), TestUtils
|
||||
assertEquals(100L, TestUtils
|
||||
.getPropertyValue(gatewayProxyFactoryBean, "defaultReplyTimeout", Expression.class).getValue());
|
||||
|
||||
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.objectToStringTransformerWithinChain.handler"));
|
||||
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.objectToMapTransformerWithinChain.handler"));
|
||||
|
||||
Object transformerHandler = this.beanFactory.getBean("subComponentsIdSupport1$child.objectToMapTransformerWithinChain.handler");
|
||||
|
||||
Object transformer = TestUtils.getPropertyValue(transformerHandler, "transformer");
|
||||
|
||||
assertThat(transformer, instanceOf(ObjectToMapTransformer.class));
|
||||
assertFalse(TestUtils.getPropertyValue(transformer, "shouldFlattenKeys", Boolean.class));
|
||||
assertSame(this.beanFactory.getBean(JsonObjectMapper.class),
|
||||
TestUtils.getPropertyValue(transformer, "jsonObjectMapper"));
|
||||
|
||||
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.mapToObjectTransformerWithinChain.handler"));
|
||||
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.controlBusWithinChain.handler"));
|
||||
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.routerWithinChain.handler"));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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,17 +16,21 @@
|
||||
|
||||
package org.springframework.integration.transformer;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -36,12 +40,19 @@ import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.support.json.Jackson2JsonObjectMapper;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gunnar Hillert
|
||||
* @author Vikas Prasad
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ObjectToMapTransformerTests {
|
||||
@@ -154,6 +165,41 @@ public class ObjectToMapTransformerTests {
|
||||
transformer.transform(message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJacksonJSR310Support_PassInstantField_ReturnsMapWithOnlyOneEntryForInstantField() throws Exception {
|
||||
Person person = new Person();
|
||||
person.deathDate = Instant.now();
|
||||
|
||||
Employee employee = new Employee();
|
||||
employee.setPerson(person);
|
||||
|
||||
Map<String, Object> transformedMap = new ObjectToMapTransformer().transformPayload(employee);
|
||||
|
||||
// If JSR310 support is enabled by calling findAndRegisterModules() on the Jackson mapper,
|
||||
// Instant field should not be broken. Thus the count should exactly be 1 here.
|
||||
assertEquals(1L, transformedMap.values().stream().filter(Objects::nonNull).count());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomMapperSupport_DisableTimestampFlag_SerializesDateAsString() throws Exception {
|
||||
Employee employee = buildEmployee();
|
||||
|
||||
ObjectMapper customMapper = new ObjectMapper();
|
||||
customMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
|
||||
|
||||
Map<String, Object> transformedMap =
|
||||
new ObjectToMapTransformer(new Jackson2JsonObjectMapper(customMapper))
|
||||
.transformPayload(employee);
|
||||
|
||||
assertThat(transformedMap.get("listOfDates[0][0]"), instanceOf(String.class));
|
||||
|
||||
assertThat(transformedMap.get("listOfDates[0][1]"), instanceOf(String.class));
|
||||
|
||||
assertThat(transformedMap.get("listOfDates[1][0]"), instanceOf(String.class));
|
||||
|
||||
assertThat(transformedMap.get("listOfDates[1][1]"), instanceOf(String.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public Employee buildEmployee() {
|
||||
Address companyAddress = new Address();
|
||||
@@ -162,8 +208,8 @@ public class ObjectToMapTransformerTests {
|
||||
companyAddress.setZip("12345");
|
||||
|
||||
Map<String, Long[]> coordinates = new HashMap<String, Long[]>();
|
||||
coordinates.put("latitude", new Long[]{(long) 1, (long) 5, (long) 13});
|
||||
coordinates.put("longitude", new Long[]{(long) 156});
|
||||
coordinates.put("latitude", new Long[] { (long) 1, (long) 5, (long) 13 });
|
||||
coordinates.put("longitude", new Long[] { (long) 156 });
|
||||
companyAddress.setCoordinates(coordinates);
|
||||
|
||||
List<Date> datesA = new ArrayList<Date>();
|
||||
@@ -231,58 +277,90 @@ public class ObjectToMapTransformerTests {
|
||||
}
|
||||
|
||||
public static class Employee {
|
||||
|
||||
private List<String> departments;
|
||||
|
||||
private List<List<Date>> listOfDates;
|
||||
|
||||
private String companyName;
|
||||
|
||||
private Person person;
|
||||
|
||||
private Address companyAddress;
|
||||
|
||||
private Map<String, Map<String, Object>> testMapInMapData;
|
||||
|
||||
public List<List<Date>> getListOfDates() {
|
||||
return listOfDates;
|
||||
}
|
||||
|
||||
public void setListOfDates(List<List<Date>> listOfDates) {
|
||||
this.listOfDates = listOfDates;
|
||||
}
|
||||
|
||||
public Map<String, Map<String, Object>> getTestMapInMapData() {
|
||||
return testMapInMapData;
|
||||
}
|
||||
|
||||
public void setTestMapInMapData(
|
||||
Map<String, Map<String, Object>> testMapInMapData) {
|
||||
this.testMapInMapData = testMapInMapData;
|
||||
}
|
||||
|
||||
public String getCompanyName() {
|
||||
return companyName;
|
||||
}
|
||||
|
||||
public void setCompanyName(String companyName) {
|
||||
this.companyName = companyName;
|
||||
}
|
||||
|
||||
public Person getPerson() {
|
||||
return person;
|
||||
}
|
||||
|
||||
public void setPerson(Person person) {
|
||||
this.person = person;
|
||||
}
|
||||
|
||||
public Address getCompanyAddress() {
|
||||
return companyAddress;
|
||||
}
|
||||
|
||||
public void setCompanyAddress(Address companyAddress) {
|
||||
this.companyAddress = companyAddress;
|
||||
}
|
||||
|
||||
public List<String> getDepartments() {
|
||||
return departments;
|
||||
}
|
||||
|
||||
public void setDepartments(List<String> departments) {
|
||||
this.departments = departments;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Person {
|
||||
|
||||
private String fname;
|
||||
|
||||
private String lname;
|
||||
|
||||
private String[] akaNames;
|
||||
|
||||
private List<Map<String, Object>> remarks;
|
||||
|
||||
private Child child;
|
||||
|
||||
private BigDecimal age;
|
||||
|
||||
private Date birthDate;
|
||||
|
||||
public Instant deathDate;
|
||||
|
||||
private Address address;
|
||||
|
||||
public BigDecimal getAge() {
|
||||
return age;
|
||||
}
|
||||
@@ -299,85 +377,112 @@ public class ObjectToMapTransformerTests {
|
||||
this.birthDate = birthDate;
|
||||
}
|
||||
|
||||
private Date birthDate;
|
||||
public Child getChild() {
|
||||
return child;
|
||||
}
|
||||
|
||||
public void setChild(Child child) {
|
||||
this.child = child;
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> getRemarks() {
|
||||
return remarks;
|
||||
}
|
||||
|
||||
public void setRemarks(List<Map<String, Object>> remarks) {
|
||||
this.remarks = remarks;
|
||||
}
|
||||
private Address address;
|
||||
|
||||
public String[] getAkaNames() {
|
||||
return akaNames;
|
||||
}
|
||||
|
||||
public void setAkaNames(String... akaNames) {
|
||||
this.akaNames = akaNames;
|
||||
}
|
||||
|
||||
public String getFname() {
|
||||
return fname;
|
||||
}
|
||||
|
||||
public void setFname(String fname) {
|
||||
this.fname = fname;
|
||||
}
|
||||
|
||||
public String getLname() {
|
||||
return lname;
|
||||
}
|
||||
|
||||
public void setLname(String lname) {
|
||||
this.lname = lname;
|
||||
}
|
||||
|
||||
public Address getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(Address address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Address {
|
||||
|
||||
private String street;
|
||||
|
||||
private String city;
|
||||
|
||||
private String zip;
|
||||
|
||||
private Map<String, List<String>> mapWithListData;
|
||||
|
||||
private Map<String, Long[]> coordinates;
|
||||
|
||||
public Map<String, List<String>> getMapWithListData() {
|
||||
return mapWithListData;
|
||||
}
|
||||
|
||||
public void setMapWithListData(Map<String, List<String>> mapWithListData) {
|
||||
this.mapWithListData = mapWithListData;
|
||||
}
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
|
||||
public void setStreet(String street) {
|
||||
this.street = street;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public void setCity(String city) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public String getZip() {
|
||||
return zip;
|
||||
}
|
||||
|
||||
public void setZip(String zip) {
|
||||
this.zip = zip;
|
||||
}
|
||||
|
||||
public Map<String, Long[]> getCoordinates() {
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
public void setCoordinates(Map<String, Long[]> coordinates) {
|
||||
this.coordinates = coordinates;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Child {
|
||||
|
||||
private Person parent;
|
||||
|
||||
public Person getParent() {
|
||||
@@ -387,5 +492,7 @@ public class ObjectToMapTransformerTests {
|
||||
public void setParent(Person parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ These will use standard Java serialization by default, but you can provide an im
|
||||
|
||||
====== Object-to-Map and Map-to-Object Transformers
|
||||
|
||||
Spring Integration also provides _Object-to-Map_ and _Map-to-Object_ transformers which utilize the Spring Expression Language (SpEL) to serialize and de-serialize the object graphs.
|
||||
Spring Integration also provides _Object-to-Map_ and _Map-to-Object_ transformers which utilize the JSON to serialize and de-serialize the object graphs.
|
||||
The object hierarchy is introspected to the most primitive types (String, int, etc.).
|
||||
The path to this type is described via SpEL, which becomes the _key_ in the transformed Map.
|
||||
The primitive type becomes the value.
|
||||
@@ -144,7 +144,7 @@ public class Child{
|
||||
|
||||
\...will be transformed to a Map which looks like this: `{person.name=George, person.child.name=Jenna, person.child.nickNames[0]=Bimbo ... etc}`
|
||||
|
||||
The SpEL-based Map allows you to describe the object structure without sharing the actual types allowing you to restore/rebuild the object graph into a differently typed Object graph as long as you maintain the structure.
|
||||
The JSON-based Map allows you to describe the object structure without sharing the actual types allowing you to restore/rebuild the object graph into a differently typed Object graph as long as you maintain the structure.
|
||||
|
||||
For example: The above structure could be easily restored back to the following Object graph via the Map-to-Object transformer:
|
||||
[source,java]
|
||||
@@ -215,7 +215,10 @@ or
|
||||
|
||||
NOTE: NOTE: 'ref' and 'type' attributes are mutually exclusive.
|
||||
You can only use one.
|
||||
Also, if using the 'ref' attribute, you must point to a 'prototype' scoped bean, otherwise a BeanCreationException will be thrown.
|
||||
Also, if using the 'ref' attribute, you must point to a 'prototype' scoped bean, otherwise a `BeanCreationException` will be thrown.
|
||||
|
||||
Starting with _version 5.0_, the `ObjectToMapTransformer` can be supplied with the customized `JsonObjectMapper`, for example in use-cases when we need special formats for dates or nulls for empty collections.
|
||||
See <<json-transformers>> for more information about `JsonObjectMapper` implementations.
|
||||
|
||||
[[stream-transformer]]
|
||||
====== Stream Transformer
|
||||
|
||||
@@ -87,7 +87,11 @@ That message is used as a `failedMessage` property of the `MessagingException` w
|
||||
|
||||
See <<transaction-synchronization>> for more information.
|
||||
|
||||
The aggregator expression-based `ReleaseStrategy` now evaluates the expression against the `MesageGroup` instead of just the collection of `Message<?>`.
|
||||
The aggregator expression-based `ReleaseStrategy` now evaluates the expression against the `MessageGroup` instead of just the collection of `Message<?>`.
|
||||
|
||||
See <<aggregator-spel>> for more information.
|
||||
|
||||
The `ObjectToMapTransformer` can now be supplied with a customised `JsonObjectMapper`.
|
||||
|
||||
See <<aggregator-spel>> for more information.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user