From 45d266972fd5b1044f25506c4b607e800dfa1cc2 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Tue, 14 Sep 2010 17:47:51 -0700 Subject: [PATCH] INT-1193 Added namespace support for JSON transformers --- .../xml/IntegrationNamespaceHandler.java | 2 + .../xml/JsonToObjectTransformerParser.java | 45 +++++ .../xml/ObjectToJsonTransformerParser.java | 43 +++++ .../config/xml/spring-integration-2.0.xsd | 78 +++++++- ...ToObjectTransformerParserTests-context.xml | 19 ++ .../JsonToObjectTransformerParserTests.java | 166 ++++++++++++++++ ...ctToJsonTransformerParserTests-context.xml | 16 ++ .../ObjectToJsonTransformerParserTests.java | 181 ++++++++++++++++++ 8 files changed, 549 insertions(+), 1 deletion(-) create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/config/xml/JsonToObjectTransformerParser.java create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/config/xml/ObjectToJsonTransformerParser.java create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/json/JsonToObjectTransformerParserTests-context.xml create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/json/JsonToObjectTransformerParserTests.java create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests-context.xml create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests.java diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java index 3af579bc94..96d12dd21a 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java @@ -43,6 +43,8 @@ public class IntegrationNamespaceHandler extends AbstractIntegrationNamespaceHan registerBeanDefinitionParser("object-to-string-transformer", new ObjectToStringTransformerParser()); registerBeanDefinitionParser("object-to-map-transformer", new ObjectToMapTransformerParser()); registerBeanDefinitionParser("map-to-object-transformer", new MapToObjectTransformerParser()); + registerBeanDefinitionParser("object-to-json-transformer", new ObjectToJsonTransformerParser()); + registerBeanDefinitionParser("json-to-object-transformer", new JsonToObjectTransformerParser()); registerBeanDefinitionParser("payload-serializing-transformer", new PayloadSerializingTransformerParser()); registerBeanDefinitionParser("payload-deserializing-transformer", new PayloadDeserializingTransformerParser()); registerBeanDefinitionParser("claim-check-in", new ClaimCheckInParser()); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/JsonToObjectTransformerParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/JsonToObjectTransformerParser.java new file mode 100644 index 0000000000..c0501b9ef2 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/JsonToObjectTransformerParser.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002-2010 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.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; + +/** + * @author Mark Fisher + * @since 2.0 + */ +public class JsonToObjectTransformerParser extends AbstractTransformerParser { + + @Override + protected String getTransformerClassName() { + return "org.springframework.integration.json.JsonToObjectTransformer"; + } + + @Override + protected void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + String type = element.getAttribute("type"); + String objectMapper = element.getAttribute("object-mapper"); + builder.addConstructorArgValue(type); + if (StringUtils.hasText(objectMapper)) { + builder.addConstructorArgReference(objectMapper); + } + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ObjectToJsonTransformerParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ObjectToJsonTransformerParser.java new file mode 100644 index 0000000000..fb06747da0 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ObjectToJsonTransformerParser.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2010 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.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; + +/** + * @author Mark Fisher + * @since 2.0 + */ +public class ObjectToJsonTransformerParser extends AbstractTransformerParser { + + @Override + protected String getTransformerClassName() { + return "org.springframework.integration.json.ObjectToJsonTransformer"; + } + + @Override + protected void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + String objectMapper = element.getAttribute("object-mapper"); + if (StringUtils.hasText(objectMapper)) { + builder.addConstructorArgReference(objectMapper); + } + } + +} diff --git a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd index 7066cebf9b..66fe1e2fd9 100644 --- a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd +++ b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd @@ -1014,7 +1014,8 @@ - + + @@ -1553,6 +1554,81 @@ + + + + Defines a Transformer that converts any Object payload to a JSON String. + + + + + + + + + + + + + + + + + + + Reference to a Jackson ObjectMapper instance to be provided optionally + if the default ObjectMapper configuration is not desirable. + + + + + + + + + + + + + + Defines a Transformer that converts a JSON String to an object. + + + + + + + + + + + + + + + + + + + + + + + + Reference to a Jackson ObjectMapper instance to be provided optionally + if the default ObjectMapper configuration is not desirable. + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/json/JsonToObjectTransformerParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/json/JsonToObjectTransformerParserTests-context.xml new file mode 100644 index 0000000000..736862809d --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/json/JsonToObjectTransformerParserTests-context.xml @@ -0,0 +1,19 @@ + + + + + + + + + + \ No newline at end of file diff --git a/spring-integration-core/src/test/java/org/springframework/integration/json/JsonToObjectTransformerParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/json/JsonToObjectTransformerParserTests.java new file mode 100644 index 0000000000..1efa1d091b --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/json/JsonToObjectTransformerParserTests.java @@ -0,0 +1,166 @@ +/* + * Copyright 2002-2010 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +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.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Mark Fisher + * @since 2.0 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class JsonToObjectTransformerParserTests { + + @Autowired + private volatile MessageChannel defaultObjectMapperInput; + + @Autowired + private volatile MessageChannel customObjectMapperInput; + + + @Test + public void defaultObjectMapper() { + String jsonString = "{\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":42,\"address\":{\"number\":123,\"street\":\"Main Street\"}}"; + QueueChannel replyChannel = new QueueChannel(); + Message message = MessageBuilder.withPayload(jsonString).setReplyChannel(replyChannel).build(); + this.defaultObjectMapperInput.send(message); + Message reply = replyChannel.receive(0); + assertNotNull(reply); + assertNotNull(reply.getPayload()); + assertEquals(TestPerson.class, reply.getPayload().getClass()); + TestPerson person = (TestPerson) reply.getPayload(); + assertEquals("John", person.getFirstName()); + assertEquals("Doe", person.getLastName()); + assertEquals(42, person.getAge()); + assertEquals("123 Main Street", person.getAddress().toString()); + } + + @Test + public void customObjectMapper() { + String jsonString = "{firstName:'John', lastName:'Doe', age:42, address:{number:123, street:'Main Street'}}"; + QueueChannel replyChannel = new QueueChannel(); + Message message = MessageBuilder.withPayload(jsonString).setReplyChannel(replyChannel).build(); + this.customObjectMapperInput.send(message); + Message reply = replyChannel.receive(0); + assertNotNull(reply); + assertNotNull(reply.getPayload()); + assertEquals(TestPerson.class, reply.getPayload().getClass()); + TestPerson person = (TestPerson) reply.getPayload(); + assertEquals("John", person.getFirstName()); + assertEquals("Doe", person.getLastName()); + assertEquals(42, person.getAge()); + assertEquals("123 Main Street", person.getAddress().toString()); + } + + + static class TestPerson { + + private String firstName; + + private String lastName; + + private int age; + + private TestAddress address; + + + public String getFirstName() { + return this.firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getLastName() { + return this.lastName; + } + + public void setAge(int age) { + this.age = age; + } + + public int getAge() { + return this.age; + } + + public void setAddress(TestAddress address) { + this.address = address; + } + + public TestAddress getAddress() { + return this.address; + } + + @Override + public String toString() { + return "name=" + this.firstName + " " + this.lastName + + ", age=" + this.age + ", address=" + this.address; + } + } + + + static class TestAddress { + + private int number; + + private String street; + + + public void setNumber(int number) { + this.number = number; + } + + public void setStreet(String street) { + this.street = street; + } + + @Override + public String toString() { + return this.number + " " + this.street; + } + } + + + static class CustomObjectMapper extends ObjectMapper { + + public CustomObjectMapper() { + this.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, Boolean.TRUE); + this.configure(Feature.ALLOW_SINGLE_QUOTES, Boolean.TRUE); + } + } + +} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests-context.xml new file mode 100644 index 0000000000..209b09a2b4 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests-context.xml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests.java new file mode 100644 index 0000000000..73545d8f1d --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests.java @@ -0,0 +1,181 @@ +/* + * Copyright 2002-2010 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.codehaus.jackson.JsonGenerator.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.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Mark Fisher + * @since 2.0 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class ObjectToJsonTransformerParserTests { + + @Autowired + private volatile MessageChannel defaultObjectMapperInput; + + @Autowired + private volatile MessageChannel customObjectMapperInput; + + + @Test + public void defaultObjectMapper() { + TestAddress address = new TestAddress(); + address.setNumber(123); + address.setStreet("Main Street"); + TestPerson person = new TestPerson(); + person.setFirstName("John"); + person.setLastName("Doe"); + person.setAge(42); + person.setAddress(address); + QueueChannel replyChannel = new QueueChannel(); + Message message = MessageBuilder.withPayload(person).setReplyChannel(replyChannel).build(); + this.defaultObjectMapperInput.send(message); + Message reply = replyChannel.receive(0); + assertNotNull(reply); + assertNotNull(reply.getPayload()); + assertEquals(String.class, reply.getPayload().getClass()); + String expected = "{\"address\":{\"number\":123,\"street\":\"Main Street\"},\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":42}"; + assertEquals(expected, reply.getPayload()); + } + + @Test + public void customObjectMapper() { + TestAddress address = new TestAddress(); + address.setNumber(123); + address.setStreet("Main Street"); + TestPerson person = new TestPerson(); + person.setFirstName("John"); + person.setLastName("Doe"); + person.setAge(42); + person.setAddress(address); + QueueChannel replyChannel = new QueueChannel(); + Message message = MessageBuilder.withPayload(person).setReplyChannel(replyChannel).build(); + this.customObjectMapperInput.send(message); + Message reply = replyChannel.receive(0); + assertNotNull(reply); + assertNotNull(reply.getPayload()); + assertEquals(String.class, reply.getPayload().getClass()); + String expected = "{address:{number:123,street:\"Main Street\"},firstName:\"John\",lastName:\"Doe\",age:42}"; + assertEquals(expected, reply.getPayload()); + } + + + static class TestPerson { + + private String firstName; + + private String lastName; + + private int age; + + private TestAddress address; + + + public String getFirstName() { + return this.firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getLastName() { + return this.lastName; + } + + public void setAge(int age) { + this.age = age; + } + + public int getAge() { + return this.age; + } + + public void setAddress(TestAddress address) { + this.address = address; + } + + public TestAddress getAddress() { + return this.address; + } + + @Override + public String toString() { + return "name=" + this.firstName + " " + this.lastName + + ", age=" + this.age + ", address=" + this.address; + } + } + + + static class TestAddress { + + private int number; + + private String street; + + + public int getNumber() { + return this.number; + } + + public void setNumber(int number) { + this.number = number; + } + + public String getStreet() { + return this.street; + } + + public void setStreet(String street) { + this.street = street; + } + + @Override + public String toString() { + return this.number + " " + this.street; + } + } + + + static class CustomObjectMapper extends ObjectMapper { + + public CustomObjectMapper() { + this.configure(Feature.QUOTE_FIELD_NAMES, Boolean.FALSE); + } + } + +}