/*** * Copyright (c) 2009 Caelum - www.caelum.com.br/opensource All rights reserved. * * 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 br.com.caelum.vraptor.serialization.gson; import static br.com.caelum.vraptor.serialization.JSONSerialization.ENVIRONMENT_INDENTED_KEY; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.TimeZone; import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; import com.google.common.collect.ForwardingCollection; import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import com.google.gson.annotations.Since; import br.com.caelum.vraptor.core.DefaultReflectionProvider; import br.com.caelum.vraptor.environment.Environment; import br.com.caelum.vraptor.interceptor.DefaultTypeNameExtractor; import br.com.caelum.vraptor.serialization.JSONPSerialization; import br.com.caelum.vraptor.serialization.JSONSerialization; import br.com.caelum.vraptor.serialization.Serializee; import br.com.caelum.vraptor.serialization.SkipSerialization; import br.com.caelum.vraptor.util.test.MockInstanceImpl; public class GsonJSONSerializationTest { private GsonJSONSerialization serialization; private ByteArrayOutputStream stream; private HttpServletResponse response; private DefaultTypeNameExtractor extractor; private Environment environment; private GsonSerializerBuilder builder; @Before public void setup() throws Exception { TimeZone.setDefault(TimeZone.getTimeZone("GMT-0300")); stream = new ByteArrayOutputStream(); response = mock(HttpServletResponse.class); when(response.getWriter()).thenReturn(new AlwaysFlushWriter(stream)); extractor = new DefaultTypeNameExtractor(); environment = mock(Environment.class); List<JsonSerializer<?>> jsonSerializers = new ArrayList<>(); List<JsonDeserializer<?>> jsonDeserializers = new ArrayList<>(); jsonSerializers.add(new CalendarGsonConverter()); jsonSerializers.add(new DateGsonConverter()); jsonSerializers.add(new CollectionSerializer()); jsonSerializers.add(new EnumSerializer()); builder = new GsonBuilderWrapper(new MockInstanceImpl<>(jsonSerializers), new MockInstanceImpl<>(jsonDeserializers), new Serializee(new DefaultReflectionProvider()), new DefaultReflectionProvider()); serialization = new GsonJSONSerialization(response, extractor, builder, environment, new DefaultReflectionProvider()); } private static class AlwaysFlushWriter extends PrintWriter { public AlwaysFlushWriter(OutputStream out) { super(out); } @Override public void write(String s) { super.write(s); super.flush(); } } @SkipSerialization public static class UserPrivateInfo { String cpf; String phone; public UserPrivateInfo(String cpf, String phone) { this.cpf = cpf; this.phone = phone; } } public static class User { String login; @SkipSerialization String password; UserPrivateInfo info; public User(String login, String password, UserPrivateInfo info) { this.login = login; this.password = password; this.info = info; } } public static class Address { @Since(1.0) String street; String city; public Address(String street) { this.street = street; } public Address(String street, String city) { this.street = street; this.city = city; } } public static class Client { @Since(1.0) String name; @Since(1.0) Address address; Calendar included; public Client(String name) { this.name = name; } public Client(String name, Address address) { this.name = name; this.address = address; } } public static class Item { String name; double price; public Item(String name, double price) { this.name = name; this.price = price; } } public static class Order { Client client; double price; String comments; List<Item> items; public Order(Client client, double price, String comments, Item... items) { this.client = client; this.price = price; this.comments = comments; this.items = Arrays.asList(items); } public String nice() { return "nice output"; } } public static class AdvancedOrder extends Order { @SuppressWarnings("unused") private final String notes; public AdvancedOrder(Client client, double price, String comments, String notes) { super(client, price, comments); this.notes = notes; } } public static class GenericWrapper<T> { Collection<T> entityList; Integer total; public GenericWrapper(Collection<T> entityList, Integer total) { this.entityList = entityList; this.total = total; } } public static class ClientAddressExclusion implements ExclusionStrategy { @Override public boolean shouldSkipField(FieldAttributes f) { return f.getName().equals("address"); } @Override public boolean shouldSkipClass(Class<?> clazz) { return false; } } @Test public void shouldSerializeGenericClass() { String expectedResult = "{\"genericWrapper\":{\"entityList\":[{\"name\":\"washington botelho\"},{\"name\":\"washington botelho\"}],\"total\":2}}"; Collection<Client> entityList = new ArrayList<>(); entityList.add(new Client("washington botelho")); entityList.add(new Client("washington botelho")); GenericWrapper<Client> wrapper = new GenericWrapper<>(entityList, entityList.size()); serialization.from(wrapper).include("entityList").serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldSerializeAllBasicFields() { String expectedResult = "{\"order\":{\"price\":15.0,\"comments\":\"pack it nicely, please\"}}"; Order order = new Order(new Client("guilherme silveira"), 15.0, "pack it nicely, please"); serialization.from(order).serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldSerializeAllBasicFieldsIndented() { String expectedResult = "{\n \"order\": {\n \"price\": 15.0,\n \"comments\": \"pack it nicely, please\"\n }\n}"; Order order = new Order(new Client("guilherme silveira"), 15.0, "pack it nicely, please"); serialization.indented().from(order).serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldIndentedWhenEnvironmentReturnsTrue() { when(environment.supports(ENVIRONMENT_INDENTED_KEY)).thenReturn(true); serialization.init(); String expectedResult = "{\n \"order\": {\n \"price\": 15.0,\n \"comments\": \"pack it nicely, please\"\n }\n}"; Order order = new Order(new Client("guilherme silveira"), 15.0, "pack it nicely, please"); serialization.from(order).serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldNotIndentedWhenEnvironmentReturnsFalse() { when(environment.supports(ENVIRONMENT_INDENTED_KEY)).thenReturn(false); serialization.init(); String expectedResult = "{\"order\":{\"price\":15.0,\"comments\":\"pack it nicely, please\"}}"; Order order = new Order(new Client("guilherme silveira"), 15.0, "pack it nicely, please"); serialization.from(order).serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldUseAlias() { String expectedResult = "{\"customOrder\":{\"price\":15.0,\"comments\":\"pack it nicely, please\"}}"; Order order = new Order(new Client("guilherme silveira"), 15.0, "pack it nicely, please"); serialization.from(order, "customOrder").serialize(); assertThat(result(), is(equalTo(expectedResult))); } public static enum Type { basic, advanced } class BasicOrder extends Order { public BasicOrder(Client client, double price, String comments, Type type) { super(client, price, comments); this.type = type; } @SuppressWarnings("unused") private final Type type; } @Test public void shouldSerializeEnumFields() { // String expectedResult = // "<basicOrder><type>basic</type><price>15.0</price><comments>pack it nicely, please</comments></basicOrder>"; Order order = new BasicOrder(new Client("guilherme silveira"), 15.0, "pack it nicely, please", Type.basic); serialization.from(order).serialize(); String result = result(); assertThat(result, containsString("\"type\":\"basic\"")); } @Test public void shouldSerializeCollection() { String expectedResult = "{\"price\":15.0,\"comments\":\"pack it nicely, please\"}"; expectedResult += "," + expectedResult; expectedResult = "{\"list\":[" + expectedResult + "]}"; Order order = new Order(new Client("guilherme silveira"), 15.0, "pack it nicely, please"); serialization.from(Arrays.asList(order, order)).serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldSerializeCollectionWithPrefixTag() { String expectedResult = "{\"price\":15.0,\"comments\":\"pack it nicely, please\"}"; expectedResult += "," + expectedResult; expectedResult = "{\"orders\":[" + expectedResult + "]}"; Order order = new Order(new Client("guilherme silveira"), 15.0, "pack it nicely, please"); serialization.from(Arrays.asList(order, order), "orders").serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldExcludeNonPrimitiveFieldsFromACollection() { Order order = new Order(new Client("guilherme silveira"), 15.0, "pack it nicely, please", new Item( "name", 12.99)); serialization.from(Arrays.asList(order, order), "orders").exclude("price").serialize(); assertThat(result(), not(containsString("\"items\""))); assertThat(result(), not(containsString("name"))); assertThat(result(), not(containsString("\"price\""))); assertThat(result(), not(containsString("12.99"))); assertThat(result(), not(containsString("15.0"))); } @Test public void shouldExcludeOnlyOmmitedFields() { User user = new User("caelum", "pwd12345", new UserPrivateInfo("123.456.789-00", "+55 (11) 1111-1111")); serialization.from(user).recursive().serialize(); assertThat(result(), not(containsString("\"pwd12345\""))); assertThat(result(), not(containsString("password"))); assertThat(result(), containsString("\"caelum\"")); assertThat(result(), containsString("login")); } @Test public void shouldExcludeOmmitedClasses() { User user = new User("caelum", "pwd12345", new UserPrivateInfo("123.456.789-00", "+55 (11) 1111-1111")); serialization.from(user).recursive().serialize(); assertThat(result(), not(containsString("info"))); assertThat(result(), not(containsString("cpf"))); assertThat(result(), not(containsString("phone"))); } static class WithAdvanced { AdvancedOrder order; } @Test public void shouldSerializeParentFields() { // String expectedResult = // "<advancedOrder><notes>complex package</notes><price>15.0</price><comments>pack it nicely, please</comments></advancedOrder>"; Order order = new AdvancedOrder(null, 15.0, "pack it nicely, please", "complex package"); serialization.from(order).serialize(); assertThat(result(), containsString("\"notes\":\"complex package\"")); } @Test public void shouldExcludeNonPrimitiveParentFields() { // String expectedResult = // "<advancedOrder><notes>complex package</notes><price>15.0</price><comments>pack it nicely, please</comments></advancedOrder>"; WithAdvanced advanced = new WithAdvanced(); advanced.order = new AdvancedOrder(new Client("john"), 15.0, "pack it nicely, please", "complex package"); serialization.from(advanced).include("order").serialize(); assertThat(result(), not(containsString("\"client\""))); } @Test public void shouldExcludeParentFields() { Order order = new AdvancedOrder(null, 15.0, "pack it nicely, please", "complex package"); serialization.from(order).exclude("comments").serialize(); assertThat(result(), not(containsString("\"comments\""))); } @Test public void shouldOptionallyExcludeFields() { String expectedResult = "{\"order\":{\"comments\":\"pack it nicely, please\"}}"; Order order = new Order(new Client("guilherme silveira"), 15.0, "pack it nicely, please"); serialization.from(order).exclude("price").serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldOptionallyIncludeFieldAndNotItsNonPrimitiveFields() { // String expectedResult = // "<order><client><name>guilherme silveira</name> </client> <price>15.0</price><comments>pack it nicely, please</comments></order>"; Order order = new Order(new Client("guilherme silveira", new Address("R. Vergueiro")), 15.0, "pack it nicely, please"); serialization.from(order).include("client").serialize(); assertThat(result(), containsString("\"name\":\"guilherme silveira\"")); assertThat(result(), not(containsString("R. Vergueiro"))); } @Test public void shouldOptionallyIncludeChildField() { // String expectedResult = // "<order><client><name>guilherme silveira</name> </client> <price>15.0</price><comments>pack it nicely, please</comments></order>"; Order order = new Order(new Client("guilherme silveira", new Address("R. Vergueiro")), 15.0, "pack it nicely, please"); serialization.from(order).include("client", "client.address").serialize(); assertThat(result(), containsString("\"street\":\"R. Vergueiro\"")); } @Test public void shouldOptionallyExcludeChildField() { // String expectedResult = // "<order><client></client> <price>15.0</price><comments>pack it nicely, please</comments></order>"; Order order = new Order(new Client("guilherme silveira"), 15.0, "pack it nicely, please"); serialization.from(order).include("client").exclude("client.name").serialize(); assertThat(result(), containsString("\"client\"")); assertThat(result(), not(containsString("guilherme silveira"))); } @Test public void shouldOptionallyIncludeListChildFields() { // String expectedResult = // "<order><client></client> <price>15.0</price><comments>pack it nicely, please</comments></order>"; Order order = new Order(new Client("guilherme silveira"), 15.0, "pack it nicely, please", new Item( "any item", 12.99)); serialization.from(order).include("items").serialize(); assertThat(result(), containsString("\"items\"")); assertThat(result(), containsString("\"name\":\"any item\"")); assertThat(result(), containsString("\"price\":12.99")); } @Test public void shouldExcludeAllPrimitiveFieldsInACollection() { String expectedResult = "{\"list\":[{},{}]}"; List<Order> orders = new ArrayList<>(); orders.add(new Order(new Client("nykolas lima"), 15.0, "gift bags, please")); orders.add(new Order(new Client("Rafael Dipold"), 15.0, "gift bags, please")); serialization.from(orders).excludeAll().serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldOptionallyExcludeFieldsFromIncludedListChildFields() { // String expectedResult = // "<order><client></client> <price>15.0</price><comments>pack it nicely, please</comments></order>"; Order order = new Order(new Client("guilherme silveira"), 15.0, "pack it nicely, please", new Item( "any item", 12.99)); serialization.from(order).include("items").exclude("items.price").serialize(); assertThat(result(), containsString("\"items\"")); assertThat(result(), containsString("\"name\":\"any item\"")); assertThat(result(), not(containsString("12.99"))); } @Test public void shouldOptionallyRemoveRoot() { Order order = new Order(new Client("guilherme silveira"), 15.0, "pack it nicely, please", new Item( "any item", 12.99)); serialization.withoutRoot().from(order).include("items").exclude("items.price").serialize(); assertThat(result(), containsString("\"items\"")); assertThat(result(), containsString("\"name\":\"any item\"")); assertThat(result(), not(containsString("12.99"))); assertThat(result(), not(containsString("\"order\":"))); } @Test public void shouldOptionallyRemoveRootIndented() { String expected = "{\n \"price\": 15.0,\n \"comments\": \"pack it nicely, please\",\n \"items\": [\n {\n \"name\": \"any item\"\n }\n ]\n}"; Order order = new Order(new Client("guilherme silveira"), 15.0, "pack it nicely, please", new Item( "any item", 12.99)); serialization.indented().withoutRoot().from(order).include("items").exclude("items.price").serialize(); assertThat(result(), equalTo(expected)); } private String result() { try { stream.flush(); return new String(stream.toByteArray()); } catch (IOException e) { throw new RuntimeException("Error flushing stream", e); } } static class MyCollection extends ForwardingCollection<Order> { @Override protected Collection<Order> delegate() { return Arrays.asList(new Order(new Client("client"), 12.22, "hoay")); } } static class CollectionSerializer implements JsonSerializer<MyCollection> { @Override public JsonElement serialize(MyCollection myColl, java.lang.reflect.Type typeOfSrc, JsonSerializationContext context) { return new JsonParser().parse("[testing]").getAsJsonArray(); } } //Expect that a ParameterizedType should be registered static class EnumSerializer implements JsonSerializer<Enum<?>> { @Override public JsonElement serialize(Enum<?> src, java.lang.reflect.Type typeOfSrc, JsonSerializationContext context) { return new JsonPrimitive(src.name()); } } @Test public void shouldUseCollectionConverterWhenItExists() { String expectedResult = "[\"testing\"]"; serialization.withoutRoot().from(new MyCollection()).serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldSerializeCalendarTimeWithISO8601() { Client c = new Client("renan"); c.included = new GregorianCalendar(2012, 8, 3, 1, 5, 9); c.included.setTimeZone(TimeZone.getTimeZone("GMT-0300")); serialization.from(c).serialize(); String result = result(); String expectedResult = "{\"client\":{\"name\":\"renan\",\"included\":\"2012-09-03T01:05:09-03:00\"}}"; assertThat(result, is(equalTo(expectedResult))); } @Test public void shouldSerializeDateWithISO8601() { Date date = new GregorianCalendar(1988, 0, 25, 1, 30, 15).getTime(); serialization.from(date).serialize(); String expectedResult = "{\"date\":\"1988-01-25T01:30:15-0300\"}"; assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldExcludeAllPrimitiveFields() { String expectedResult = "{\"order\":{}}"; Order order = new Order(new Client("nykolas lima"), 15.0, "gift bags, please"); serialization.from(order).excludeAll().serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldExcludeAllPrimitiveParentFields() { String expectedResult = "{\"advancedOrder\":{}}"; Order order = new AdvancedOrder(null, 15.0, "pack it nicely, please", "complex package"); serialization.from(order).excludeAll().serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldExcludeAllThanIncludeAndSerialize() { String expectedResult = "{\"order\":{\"price\":15.0}}"; Order order = new Order(new Client("nykolas lima"), 15.0, "gift bags, please"); serialization.from(order).excludeAll().include("price").serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldSerializeWithCallback() { JSONPSerialization serialization = new GsonJSONPSerialization(response, extractor, builder, environment, new DefaultReflectionProvider()); String expectedResult = "calculate({\"order\":{\"price\":15.0}})"; Order order = new Order(new Client("nykolas lima"), 15.0, "gift bags, please"); serialization.withCallback("calculate").from(order).excludeAll().include("price").serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldSerializeVersionedJsonFieldsWithSinceAnnotation() { JSONSerialization serialization = new GsonJSONSerialization(response, extractor, builder, environment, new DefaultReflectionProvider()); String expectedResult = "{\"client\":{\"name\":\"adolfo eloy\"}}"; Client client = new Client("adolfo eloy"); serialization.version(1.0).from(client).serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldSerializeRecursivelyVersionedJsonFields() { JSONSerialization serialization = new GsonJSONSerialization(response, extractor, builder, environment, new DefaultReflectionProvider()); String expectedResult = "{\"client\":{\"name\":\"adolfo eloy\",\"address\":{\"street\":\"antonio agu\",\"city\":\"osasco\"}}}"; Client client = new Client("adolfo eloy", new Address("antonio agu", "osasco")); serialization.version(1.0).from(client).recursive().serialize(); assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldSerializeNullfieldswhenRequested(){ Address address = new Address("Alameda street", null); serialization.serializeNulls().from(address).serialize(); String expectedResult = "{\"address\":{\"street\":\"Alameda street\",\"city\":null}}"; assertThat(result(), is(equalTo(expectedResult))); } @Test public void shouldNotSerializeNullFieldsByDefault(){ Address address = new Address("Alameda street", null); serialization.from(address).serialize(); String expectedResult = "{\"address\":{\"street\":\"Alameda street\"}}"; assertThat(result(), is(equalTo(expectedResult))); } }