/***
* 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 org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import javax.enterprise.inject.Instance;
import javax.servlet.http.HttpServletRequest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializer;
import br.com.caelum.vraptor.Consumes;
import br.com.caelum.vraptor.controller.BeanClass;
import br.com.caelum.vraptor.controller.ControllerMethod;
import br.com.caelum.vraptor.controller.DefaultBeanClass;
import br.com.caelum.vraptor.controller.DefaultControllerMethod;
import br.com.caelum.vraptor.core.DefaultReflectionProvider;
import br.com.caelum.vraptor.http.ParameterNameProvider;
import br.com.caelum.vraptor.http.ParanamerNameProvider;
import br.com.caelum.vraptor.ioc.Container;
import br.com.caelum.vraptor.serialization.Deserializee;
import br.com.caelum.vraptor.serialization.Serializee;
import br.com.caelum.vraptor.util.test.MockInstanceImpl;
import br.com.caelum.vraptor.view.GenericController;
import net.vidageek.mirror.dsl.Mirror;
public class GsonDeserializerTest {
@Rule
public ExpectedException exception = ExpectedException.none();
private GsonDeserializerBuilder builder;
private GsonDeserialization deserializer;
private ParameterNameProvider provider;
private HttpServletRequest request;
private ControllerMethod dogParameter;
private ControllerMethod dogAndIntegerParameter;
private ControllerMethod noParameter;
private ControllerMethod listDog;
private ControllerMethod integerAndDogParameter;
private ControllerMethod dateParameter;
private ControllerMethod dogParameterWithoutRoot;
private ControllerMethod dogParameterNameEqualsJsonPropertyWithoutRoot;
private @Mock Container container;
private @Mock Instance<Deserializee> deserializeeInstance;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
TimeZone.setDefault(TimeZone.getTimeZone("GMT-0300"));
provider = new ParanamerNameProvider();
request = mock(HttpServletRequest.class);
List<JsonDeserializer<?>> jsonDeserializers = new ArrayList<>();
List<JsonSerializer<?>> jsonSerializers = new ArrayList<>();
jsonDeserializers.add(new CalendarGsonConverter());
jsonDeserializers.add(new DateGsonConverter());
builder = new GsonBuilderWrapper(new MockInstanceImpl<>(jsonSerializers), new MockInstanceImpl<>(jsonDeserializers),
new Serializee(new DefaultReflectionProvider()), new DefaultReflectionProvider());
deserializer = new GsonDeserialization(builder, provider, request, container, deserializeeInstance);
BeanClass controllerClass = new DefaultBeanClass(DogController.class);
noParameter = new DefaultControllerMethod(controllerClass, DogController.class.getDeclaredMethod("noParameter"));
dogParameter = new DefaultControllerMethod(controllerClass, DogController.class.getDeclaredMethod("dogParameter", Dog.class));
dateParameter = new DefaultControllerMethod(controllerClass, DogController.class.getDeclaredMethod("dateParameter", Date.class));
dogAndIntegerParameter = new DefaultControllerMethod(controllerClass, DogController.class.getDeclaredMethod("dogAndIntegerParameter", Dog.class,
Integer.class));
integerAndDogParameter = new DefaultControllerMethod(controllerClass, DogController.class.getDeclaredMethod("integerAndDogParameter",
Integer.class, Dog.class));
listDog = new DefaultControllerMethod(controllerClass, DogController.class.getDeclaredMethod("list", List.class));
dogParameterWithoutRoot = new DefaultControllerMethod(controllerClass, DogController.class.getDeclaredMethod("dogParameterWithoutRoot", Dog.class));
dogParameterNameEqualsJsonPropertyWithoutRoot = new DefaultControllerMethod(controllerClass, DogController.class.getDeclaredMethod("dogParameterNameEqualsJsonPropertyWithoutRoot", Dog.class));
when(deserializeeInstance.get()).thenReturn(new Deserializee());
when(container.instanceFor(WithRoot.class)).thenReturn(new WithRoot());
when(container.instanceFor(WithoutRoot.class)).thenReturn(new WithoutRoot());
}
static class Dog {
private String name;
private Integer age;
private Calendar birthday;
}
static class DogController {
public void noParameter() {}
@Consumes(options=WithRoot.class)
public void dogParameter(Dog dog) {}
@Consumes
public void dogParameterWithoutRoot(Dog dog) {}
@Consumes(options=WithoutRoot.class)
public void dogParameterNameEqualsJsonPropertyWithoutRoot(Dog name) {}
@Consumes
public void dogAndIntegerParameter(Dog dog, Integer times) {}
@Consumes
public void integerAndDogParameter(Integer times, Dog dog) {}
@Consumes
public void dateParameter(Date date) {}
@Consumes
public void list(List<Dog> dogs) {}
}
private class DogDeserializer implements JsonDeserializer<Dog> {
@Override
public Dog deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
Dog dog = new Dog();
dog.name = "Renan";
dog.age = 25;
return dog;
}
}
static class DogGenericController extends GenericController<Dog> {
}
@Test
public void shouldDeserializerParseArraysWithoutRoot(){
InputStream stream = asStream(
"[" +
"{'name':'name1','age':1}," +
"{'name':'name2','age':2}," +
"{'name':'name3','age':3}" +
"]");
Object[] deserialized = deserializer.deserialize(stream, listDog);
List<Dog> dogs = (List<Dog>) deserialized[0];
assertThat(dogs.size(), is(3));
assertThat(dogs.get(0), is(instanceOf(Dog.class)));
Dog dog = (Dog) dogs.get(0);
assertThat(dog.name, is("name1"));
assertThat(dog.age, is(1));
}
@Test
public void shouldDeserializerParseArraysWithRoot(){
InputStream stream = asStream(
"{dogs : [" +
"{'name':'name1','age':1}," +
"{'name':'name2','age':2}," +
"{'name':'name3','age':3}" +
"]}");
Object[] deserialized = deserializer.deserialize(stream, listDog);
List<Dog> dogs = (List<Dog>) deserialized[0];
assertThat(dogs.size(), is(3));
assertThat(dogs.get(0), is(instanceOf(Dog.class)));
Dog dog = (Dog) dogs.get(0);
assertThat(dog.name, is("name1"));
assertThat(dog.age, is(1));
}
@Test
public void shouldNotAcceptMethodsWithoutArguments() throws Exception {
exception.expect(IllegalArgumentException.class);
exception.expectMessage("Methods that consumes representations must receive just one argument");
deserializer.deserialize(emptyStream(), noParameter);
}
@Test
public void shouldBeAbleToDeserializeADog() throws Exception {
InputStream stream = asStream("{'dog':{'name':'Brutus','age':7}}");
Object[] deserialized = deserializer.deserialize(stream, dogParameter);
assertThat(deserialized.length, is(1));
assertThat(deserialized[0], is(instanceOf(Dog.class)));
Dog dog = (Dog) deserialized[0];
assertThat(dog.name, is("Brutus"));
assertThat(dog.age, is(7));
}
@Test
public void shouldBeAbleToDeserializeADogWithoutRootAndParameterNameEqualsJsonProperty() throws Exception {
InputStream stream = asStream("{'name':'Brutus','age':7}");
Object[] deserialized = deserializer.deserialize(stream, dogParameterNameEqualsJsonPropertyWithoutRoot);
assertThat(deserialized.length, is(1));
assertThat(deserialized[0], is(instanceOf(Dog.class)));
Dog dog = (Dog) deserialized[0];
assertThat(dog.name, is("Brutus"));
assertThat(dog.age, is(7));
}
@Test
public void shouldBeAbleToDeserializeADogWithoutRoot() throws Exception {
InputStream stream = asStream("{'name':'Brutus','age':7}");
Object[] deserialized = deserializer.deserialize(stream, dogParameterWithoutRoot);
assertThat(deserialized.length, is(1));
assertThat(deserialized[0], is(instanceOf(Dog.class)));
Dog dog = (Dog) deserialized[0];
assertThat(dog.name, is("Brutus"));
assertThat(dog.age, is(7));
}
@Test
public void shouldBeAbleToDeserializeADogWithDeserializerAdapter() throws Exception {
List<JsonDeserializer<?>> deserializers = new ArrayList<>();
List<JsonSerializer<?>> serializers = new ArrayList<>();
deserializers.add(new DogDeserializer());
builder = new GsonBuilderWrapper(new MockInstanceImpl<>(serializers), new MockInstanceImpl<>(deserializers),
new Serializee(new DefaultReflectionProvider()), new DefaultReflectionProvider());
deserializer = new GsonDeserialization(builder, provider, request, container, deserializeeInstance);
InputStream stream = asStream("{'dog':{'name':'Renan Reis','age':'0'}}");
Object[] deserialized = deserializer.deserialize(stream, dogParameter);
assertThat(deserialized.length, is(1));
assertThat(deserialized[0], is(instanceOf(Dog.class)));
Dog dog = (Dog) deserialized[0];
assertThat(dog.name, is("Renan"));
assertThat(dog.age, is(25));
}
@Test
public void shouldBeAbleToDeserializeADogWhenMethodHasMoreThanOneArgument() throws Exception {
InputStream stream = asStream("{'dog':{'name':'Brutus','age':7}}");
Object[] deserialized = deserializer.deserialize(stream, dogAndIntegerParameter);
assertThat(deserialized.length, is(2));
assertThat(deserialized[0], is(instanceOf(Dog.class)));
Dog dog = (Dog) deserialized[0];
assertThat(dog.name, is("Brutus"));
assertThat(dog.age, is(7));
}
@Test
public void shouldBeAbleToDeserializeADogWhenMethodHasMoreThanOneArgumentAndJsonIsTheLastOne() throws Exception {
InputStream stream = asStream("{'dog':{'name':'Brutus','age':7}}");
Object[] deserialized = deserializer.deserialize(stream, integerAndDogParameter);
assertThat(deserialized.length, is(2));
assertThat(deserialized[1], is(instanceOf(Dog.class)));
Dog dog = (Dog) deserialized[1];
assertThat(dog.name, is("Brutus"));
assertThat(dog.age, is(7));
}
@Test
public void shouldBeAbleToDeserializeADogNamedDifferently() throws Exception {
InputStream stream = asStream("{'dog':{'name':'Brutus','age':7}}");
Object[] deserialized = deserializer.deserialize(stream, dogParameter);
assertThat(deserialized.length, is(1));
assertThat(deserialized[0], is(instanceOf(Dog.class)));
Dog dog = (Dog) deserialized[0];
assertThat(dog.name, is("Brutus"));
assertThat(dog.age, is(7));
}
@Test
public void shouldHonorRequestHeaderAcceptCharset() throws Exception {
InputStream stream = asStream("{'dog':{'name':'ç'}}", StandardCharsets.ISO_8859_1);
when(request.getHeader("Accept-Charset")).thenReturn("UTF-8,*;q=0.5");
Object[] deserialized = deserializer.deserialize(stream, dogParameter);
assertThat(deserialized.length, is(1));
assertThat(deserialized[0], is(instanceOf(Dog.class)));
Dog dog = (Dog) deserialized[0];
assertThat(dog.name, is("ç"));
}
@Test
public void whenNoCharsetHeaderIsFoundThanAssumeItIsUTF8() throws Exception {
InputStream stream = asStream("{'dog':{'name':'ç'}}", StandardCharsets.ISO_8859_1);
when(request.getHeader("Accept-Charset")).thenReturn(null);
Object[] deserialized = deserializer.deserialize(stream, dogParameter);
assertThat(deserialized.length, is(1));
assertThat(deserialized[0], is(instanceOf(Dog.class)));
Dog dog = (Dog) deserialized[0];
assertThat(dog.name, is("ç"));
}
@Test
public void shouldByPassDeserializationWhenHasNoContent() {
Object[] deserialized = deserializer.deserialize(emptyStream(), dogParameter);
assertThat(deserialized.length, is(1));
assertThat(deserialized[0], is(nullValue()));
}
@Test
public void shouldBeAbleToDeserializeADogWhenMethodHasMoreThanOneArgumentAndHasNotRoot() {
InputStream stream = asStream("{'name':'Brutus','age':7}");
Object[] deserialized = deserializer.deserialize(stream, dogAndIntegerParameter);
assertThat(deserialized[0], is(instanceOf(Dog.class)));
Dog dog = (Dog) deserialized[0];
assertThat(dog.name, is("Brutus"));
assertThat(dog.age, is(7));
}
@Test
public void shouldDeserializeFromGenericTypeOneParam() {
InputStream stream = asStream("{'entity':{'name':'Brutus','age':7,'birthday':'2013-07-23T17:14:14-03:00'}}");
BeanClass resourceClass = new DefaultBeanClass(DogGenericController.class);
Method method = new Mirror().on(DogGenericController.class).reflect().method("method").withAnyArgs();
ControllerMethod resource = new DefaultControllerMethod(resourceClass, method);
Object[] deserialized = deserializer.deserialize(stream, resource);
Dog dog = (Dog) deserialized[0];
assertThat(dog.name, equalTo("Brutus"));
}
@Test
public void shouldDeserializeFromGenericTypeWithoutRoot() {
InputStream stream = asStream("{'name':'Brutus','age':7,'birthday':'2013-07-23T17:14:14-03:00'}");
BeanClass resourceClass = new DefaultBeanClass(DogGenericController.class);
Method method = new Mirror().on(DogGenericController.class).reflect().method("method").withAnyArgs();
ControllerMethod resource = new DefaultControllerMethod(resourceClass, method);
Object[] deserialized = deserializer.deserialize(stream, resource);
Dog dog = (Dog) deserialized[0];
assertThat(dog.name, equalTo("Brutus"));
assertThat(dog.age, equalTo(7));
}
@Test
public void shouldDeserializeFromGenericTypeTwoParams() {
InputStream stream = asStream("{'entity':{'name':'Brutus','age':7,'birthday':'2013-07-23T17:14:14-03:00'}, 'param': 'test', 'over': 'value'}");
BeanClass resourceClass = new DefaultBeanClass(DogGenericController.class);
Method method = new Mirror().on(DogGenericController.class).reflect().method("anotherMethod").withAnyArgs();
ControllerMethod resource = new DefaultControllerMethod(resourceClass, method);
Object[] deserialized = deserializer.deserialize(stream, resource);
Dog dog = (Dog) deserialized[0];
String param = (String) deserialized[1];
assertThat(dog.name, equalTo("Brutus"));
assertThat(param, equalTo("test"));
assertThat(deserialized.length, equalTo(2));
}
@Test
public void shouldDeserializeADogWithCalendarWithISO8601() {
InputStream stream = asStream("{'dog':{'name':'Otto','age':2,'birthday':'2013-07-23T17:14:14-03:00'}}");
Object[] deserialized = deserializer.deserialize(stream, dogParameter);
assertThat(deserialized.length, is(1));
assertThat(deserialized[0], is(instanceOf(Dog.class)));
Dog dog = (Dog) deserialized[0];
assertThat(dog.name, is("Otto"));
assertThat(dog.age, is(2));
Calendar birthday = new GregorianCalendar(2013, 6, 23, 17, 14, 14);
birthday.setTimeZone(TimeZone.getTimeZone("GMT-0300"));
// calendar.equals is too bad :)
assertThat(dog.birthday.compareTo(birthday), is(0));
}
@Test
public void shouldDeserializeADateWithISO8601() {
InputStream stream = asStream("{\"date\":\"1988-02-25T02:30:15 -0300\"}");
Object[] deserialized = deserializer.deserialize(stream, dateParameter);
assertThat(deserialized.length, is(1));
assertThat(deserialized[0], is(instanceOf(Date.class)));
Date deserializedDate = (Date) deserialized[0];
Date date = new GregorianCalendar(1988, 1, 25, 2, 30, 15).getTime();
assertEquals(date, deserializedDate);
}
private InputStream asStream(String str, Charset charset) {
return new ByteArrayInputStream(str.getBytes(charset));
}
private InputStream asStream(String str) {
return asStream(str, Charset.defaultCharset());
}
private InputStream emptyStream() {
return new ByteArrayInputStream(new byte[0]);
}
}