/*-
* Copyright 2016 Diamond Light Source Ltd.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.dawnsci.json.test;
import static org.eclipse.dawnsci.json.test.JsonUtils.assertJsonEquals;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringEscapeUtils;
import org.eclipse.dawnsci.analysis.api.persistence.IMarshallerService;
import org.eclipse.dawnsci.json.MarshallerService;
import org.eclipse.dawnsci.json.test.classregistry.TestObjectClassRegistry;
import org.eclipse.dawnsci.json.test.testobject.Animal;
import org.eclipse.dawnsci.json.test.testobject.Bird;
import org.eclipse.dawnsci.json.test.testobject.Cat;
import org.eclipse.dawnsci.json.test.testobject.CatWrapper;
import org.eclipse.dawnsci.json.test.testobject.ContainerBean;
import org.eclipse.dawnsci.json.test.testobject.Person;
import org.eclipse.dawnsci.json.test.testobject.ProjectBean;
import org.eclipse.dawnsci.json.test.testobject.TestStatusBean;
import org.eclipse.dawnsci.json.test.testobject.TestTypeNonRegisteredImpl;
import org.hamcrest.CoreMatchers;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.MockitoAnnotations;
import com.fasterxml.jackson.databind.JsonMappingException;
/**
* Unit tests for the Jackson JSON marshaller which check the behaviour in a simulated OSGi environment where classes
* need to be loaded from the correct bundles.
* <p>
* If the marshaller settings are changed, the new JSON string produced in each test can be written to std out by
* uncommenting the relevant line in tearDown(), allowing it to be copied into the Java code to update the tests.
*
* @author Colin Palmer
* @author Martin Gaughran
*
*/
public class JsonMarshallerCustomClassesTest {
private static final String JSON_FOR_JIM = "{\n \"@type\" : \"jsontest.person\",\n \"name\" : \"Jim\",\n \"pet\" : {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n }\n}";
private static final String JSON_FOR_JOHN = "{\n \"@type\" : \"jsontest.person\",\n \"name\" : \"John\",\n \"pet\" : {\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n }\n}";
private static final String JSON_FOR_JAMES = "{\n \"@type\" : \"jsontest.person\",\n \"name\" : \"James\" \n}";
private static final String JSON_FOR_FELIX = "{\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n}";
private static final String JSON_FOR_ANIMAL_ARRAY = "[ {\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n}, {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n}, {\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n} ]";
private static final String JSON_FOR_OBJECT_ARRAY = "[ {\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n}, {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n}, {\n \"@type\" : \"jsontest.person\",\n \"name\" : \"Jim\",\n \"pet\" : {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n }\n} ]";
private static final String JSON_FOR_ANIMAL_LIST = "[ {\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n}, {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n}, {\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n} ]";
private static final String JSON_FOR_OBJECT_LIST = "[ {\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n}, {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n}, {\n \"@type\" : \"jsontest.person\",\n \"name\" : \"Jim\",\n \"pet\" : {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n }\n} ]";
private static final String JSON_FOR_OBJECT_SET = "[ {\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n}, {\n \"@type\" : \"jsontest.person\",\n \"name\" : \"Jim\",\n \"pet\" : {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n }\n}, {\n \"@type\" : \"jsontest.person\",\n \"name\" : \"Jim\",\n \"pet\" : {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n }\n} ]";
private static final String JSON_FOR_OBJECT_MAP = "{\n \"Polly\" : {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n },\n \"Felix\" : {\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n },\n \"John\" : {\n \"@type\" : \"jsontest.person\",\n \"name\" : \"John\",\n \"pet\" : {\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n }\n },\n \"Jim\" : {\n \"@type\" : \"jsontest.person\",\n \"name\" : \"Jim\",\n \"pet\" : {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n }\n }\n}";
// Used to test marshalling without using the class registry-based id resolver.
private static final String JSON_FOR_NESTED_STATIC_ATTRIBUTE_WITHOUT_TYPE_INFO = "{ \"cat\" : { \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\" } }";
private static final String JSON_FOR_STATIC_OBJECT_WITHOUT_TYPE_INFO = "{ \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\" }";
// An example of a bean used by Xia2 which could be sent by another process and must deserialize correctly in current version
private static final String JSON_FOR_PROJECT_BEAN = "{\"status\":\"COMPLETE\",\"name\":\"X1_weak_M1S1_1 - X1_weak_M1S1_1\",\"message\":\"Xia2 run completed normally\",\"percentComplete\":100.0,\"userName\":\"awa25\",\"hostName\":\"cs04r-sc-vserv-45.diamond.ac.uk\",\"runDirectory\":\"/dls/i03/data/2016/cm14451-1/processed/tmp/2016-01-27/fake085224/MultiCrystal_1\",\"uniqueId\":\"1453910139320_94ed2a2b-997e-4dbc-ad6e-0c3c04bb2c82\",\"submissionTime\":1453910139340,\"properties\":null,\"projectName\":\"MultiCrystalRerun\",\"cystalName\":\"fake085224\",\"sweeps\":[{\"name\":\"X1_weak_M1S1_1\",\"sessionId\":\"55167\",\"dataCollectionId\":\"1007379\",\"imageDirectory\":\"/dls/i03/data/2016/cm14451-1/tmp/2016-01-27/fake085224/\",\"firstImageName\":\"X1_weak_M1S1_1_0001.cbf\",\"start\":1,\"end\":900,\"wavelength\":0.979493,\"xBeam\":212.51,\"yBeam\":219.98,\"resolution\":null}],\"wavelength\":\"NaN\",\"commandLineSwitches\":\"\",\"anomalous\":true,\"spaceGroup\":null,\"unitCell\":null,\"resolution\":null}";
private static final String JSON_FOR_PROJECT_BEAN_WITH_TYPES = "{\n \"@type\" : \"jsontest.projectbean\",\n \"uniqueId\" : \"1453910139320_94ed2a2b-997e-4dbc-ad6e-0c3c04bb2c82\",\n \"status\" : [ \"jsontest.teststatus\", \"COMPLETE\" ],\n \"name\" : \"X1_weak_M1S1_1 - X1_weak_M1S1_1\",\n \"message\" : \"Xia2 run completed normally\",\n \"percentComplete\" : 100.0,\n \"userName\" : \"awa25\",\n \"hostName\" : \"cs04r-sc-vserv-45.diamond.ac.uk\",\n \"runDirectory\" : \"/dls/i03/data/2016/cm14451-1/processed/tmp/2016-01-27/fake085224/MultiCrystal_1\",\n \"submissionTime\" : 1453910139340,\n \"projectName\" : \"MultiCrystalRerun\",\n \"cystalName\" : \"fake085224\",\n \"sweeps\" : [ {\n \"@type\" : \"jsontest.sweepbean\",\n \"name\" : \"X1_weak_M1S1_1\",\n \"sessionId\" : \"55167\",\n \"dataCollectionId\" : \"1007379\",\n \"imageDirectory\" : \"/dls/i03/data/2016/cm14451-1/tmp/2016-01-27/fake085224/\",\n \"firstImageName\" : \"X1_weak_M1S1_1_0001.cbf\",\n \"start\" : 1,\n \"end\" : 900,\n \"wavelength\" : 0.979493,\n \"xBeam\" : 212.51,\n \"yBeam\" : 219.98\n } ],\n \"wavelength\" : \"NaN\",\n \"commandLineSwitches\" : \"\",\n \"anomalous\" : true\n}";
// Json for 'contained' types (non-generic wrapper class
private static final String JSON_FOR_CONTAINED_OBJECT_ARRAY = "{\n \"@type\" : \"jsontest.containerbean\",\n \"objArray\" : [ {\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n}, {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n}, {\n \"@type\" : \"jsontest.person\",\n \"name\" : \"Jim\",\n \"pet\" : {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n }\n} ] \n}";
private static final String JSON_FOR_CONTAINED_OBJECT_LIST = "{\n \"@type\" : \"jsontest.containerbean\",\n \"objList\" : [ {\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n}, {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n}, {\n \"@type\" : \"jsontest.person\",\n \"name\" : \"Jim\",\n \"pet\" : {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n }\n} ] \n}";
private static final String JSON_FOR_CONTAINED_OBJECT_MAP = "{\n \"@type\" : \"jsontest.containerbean\",\n \"objMap\" : {\n \"Polly\" : {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n },\n \"Felix\" : {\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n },\n \"John\" : {\n \"@type\" : \"jsontest.person\",\n \"name\" : \"John\",\n \"pet\" : {\n \"@type\" : \"jsontest.animal.cat\",\n \"name\" : \"Felix\",\n \"whiskers\" : \"Luxuriant\"\n }\n },\n \"Jim\" : {\n \"@type\" : \"jsontest.person\",\n \"name\" : \"Jim\",\n \"pet\" : {\n \"@type\" : \"jsontest.animal.bird\",\n \"name\" : \"Polly\",\n \"feathers\" : \"Green\"\n }\n }\n} \n}";
// Json for a range of error tests
private static final String JSON_FOR_UNKNOWN_TOP_LEVEL_CLASS_ID = "{ \"@type\" : \"jsontest.doesnotexist\", \"string\" : \"Non-registered, top level class.\" }";
private static final String JSON_FOR_NO_TOP_LEVEL_CLASS_ID = "{ \"string\" : \"Non-registered, top level class.\" }";
private static final String JSON_FOR_UNKNOWN_NESTED_DYNAMIC_DEFINED_CLASS_ID = "{ \"@type\" : \"jsontest.person\", \"pet\" : { \"@type\" : \"jsontest.nonexistent\", \"whiskers\" : \"Luxuriant\", \"name\" : \"Tiddles\" } }";
private static final String JSON_FOR_NESTED_DYNAMIC_DEFINED_CLASS_WITH_NO_ID = "{ \"@type\" : \"jsontest.person\", \"pet\" : { \"whiskers\" : \"Luxuriant\", \"name\" : \"Tiddles\" } }";
private static final String JSON_FOR_UNKNOWN_NESTED_STATIC_DEFINED_CLASS_ID = "{ \"cat\" : { \"@type\" : \"jsontest.nonexistentcat\", \"whiskers\" : \"Luxuriant\", \"name\" : \"Tiddles\" } }";
private static final String JSON_FOR_STATIC_DEFINED_CLASS_WITH_NO_ID = "{ \"cat\" : { \"whiskers\" : \"Luxuriant\", \"name\" : \"Tiddles\" } }";
private IMarshallerService marshaller;
private IMarshallerService marshallerWithNoRegistry;
private String json;
// Test objects
private Bird polly;
private Cat felix;
private Person jim;
private Person john;
private Person james;
@Before
public void setUp() throws Exception {
createTestObjects();
MockitoAnnotations.initMocks(this);
marshaller = new MarshallerService(new TestObjectClassRegistry());
marshallerWithNoRegistry = new MarshallerService();
}
private void createTestObjects() {
polly = new Bird();
polly.setName("Polly");
polly.setFeathers("Green");
felix = new Cat();
felix.setName("Felix");
felix.setWhiskers("Luxuriant");
jim = new Person();
jim.setName("Jim");
jim.setPet(polly);
john = new Person();
john.setName("John");
john.setPet(felix);
james = new Person();
james.setName("James");
james.setPet(null);
}
@After
public void tearDown() throws Exception {
if (json != null) {
// So we can see what's going on
// System.out.println("JSON: " + json);
// To make it easy to replace expected JSON values in the code when we're sure they're correct
@SuppressWarnings("unused")
String javaLiteralForJSONString = '"' + StringEscapeUtils.escapeJava(json) + '"';
// System.out.println("Java literal:\n" + javaLiteralForJSONString);
}
json = null;
marshaller = null;
}
@Test
public void testSerializationOfJim() throws Exception {
json = marshaller.marshal(jim);
assertJsonEquals(JSON_FOR_JIM, json);
}
@Test
public void testDeserialisationOfJim() throws Exception {
Person deserializedJim = marshaller.unmarshal(JSON_FOR_JIM, Person.class);
assertEquals("Jim", deserializedJim.getName());
assertThat(deserializedJim.getPet(), is(instanceOf(Bird.class)));
Bird deserializedPolly = (Bird) deserializedJim.getPet();
assertEquals("Polly", deserializedPolly.getName());
assertEquals("Green", deserializedPolly.getFeathers());
}
@Test
public void testDeserialisationOfJimAsNull() throws Exception {
Person deserializedJim = marshaller.unmarshal(JSON_FOR_JIM, null);
assertEquals("Jim", deserializedJim.getName());
assertThat(deserializedJim.getPet(), is(instanceOf(Bird.class)));
Bird deserializedPolly = (Bird) deserializedJim.getPet();
assertEquals("Polly", deserializedPolly.getName());
assertEquals("Green", deserializedPolly.getFeathers());
}
@Test
public void testSerializationOfJamesWithNullPet() throws Exception {
json = marshaller.marshal(james);
assertJsonEquals(JSON_FOR_JAMES, json);
}
@Test
public void testDeserialisationOfJamesWithNullPet() throws Exception {
Person deserializedJames = marshaller.unmarshal(JSON_FOR_JAMES, Person.class);
assertEquals("James", deserializedJames.getName());
assertThat(deserializedJames.getPet(), is(nullValue()));
}
@Test
public void testSerializationOfJohn() throws Exception {
json = marshaller.marshal(john);
assertJsonEquals(JSON_FOR_JOHN, json);
}
@Test
public void testDeserialisationOfJohn() throws Exception {
Person deserializedJohn = marshaller.unmarshal(JSON_FOR_JOHN, Person.class);
assertEquals("John", deserializedJohn.getName());
assertThat(deserializedJohn.getPet(), is(instanceOf(Cat.class)));
Cat deserializedFelix = (Cat) deserializedJohn.getPet();
assertEquals("Felix", deserializedFelix.getName());
assertEquals("Luxuriant", deserializedFelix.getWhiskers());
}
@Test
public void testDeserialisationOfJohnAsNull() throws Exception {
Person deserializedJohn = marshaller.unmarshal(JSON_FOR_JOHN, null);
assertEquals("John", deserializedJohn.getName());
assertThat(deserializedJohn.getPet(), is(instanceOf(Cat.class)));
Cat deserializedFelix = (Cat) deserializedJohn.getPet();
assertEquals("Felix", deserializedFelix.getName());
assertEquals("Luxuriant", deserializedFelix.getWhiskers());
}
@Test(expected = IllegalArgumentException.class)
public void testDeserialisationOfJohnAsAnimal() throws Exception {
marshaller.unmarshal(JSON_FOR_JOHN, Animal.class);
}
@Test
public void testSerialisationOfFelix() throws Exception {
json = marshaller.marshal(felix);
assertJsonEquals(JSON_FOR_FELIX, json);
}
@Test
public void testDeserialisationOfFelixAsAnimal() throws Exception {
Animal deserializedFelix = marshaller.unmarshal(JSON_FOR_FELIX, Animal.class);
assertEquals("Felix", deserializedFelix.getName());
assertThat(deserializedFelix, is(instanceOf(Cat.class)));
assertEquals("Luxuriant", ((Cat) deserializedFelix).getWhiskers());
}
@Test
public void testDeserialisationOfFelixAsNull() throws Exception {
Animal deserializedFelix = marshaller.unmarshal(JSON_FOR_FELIX, null);
assertEquals("Felix", deserializedFelix.getName());
assertThat(deserializedFelix, is(instanceOf(Cat.class)));
assertEquals("Luxuriant", ((Cat) deserializedFelix).getWhiskers());
}
@Test
public void testArraySerialization() throws Exception {
Object[] animalArray = new Animal[] { felix, polly, felix };
json = marshaller.marshal(animalArray);
assertJsonEquals(JSON_FOR_ANIMAL_ARRAY, json);
}
@Test
public void testArrayDeserializationAsAnimalArray() throws Exception {
Animal[] animalArray = marshaller.unmarshal(JSON_FOR_ANIMAL_ARRAY, Animal[].class);
assertThat(animalArray[0], is(instanceOf(Cat.class)));
assertThat(animalArray[1], is(instanceOf(Bird.class)));
assertThat(animalArray[2], is(instanceOf(Cat.class)));
assertThat(animalArray[0].getName(), is("Felix"));
}
@Test
public void testArrayDeserializationAnimalAsobjectArr() throws Exception {
Object[] objectArr = marshaller.unmarshal(JSON_FOR_ANIMAL_ARRAY, Object[].class);
assertThat(objectArr[0], is(instanceOf(Cat.class)));
assertThat(objectArr[1], is(instanceOf(Bird.class)));
assertThat(objectArr[2], is(instanceOf(Cat.class)));
assertThat(((Cat) objectArr[0]).getWhiskers(), is(equalTo("Luxuriant")));
}
@Test
public void testObjectArraySerialization() throws Exception {
Object[] objectArr = new Object[] { felix, polly, jim };
json = marshaller.marshal(objectArr);
assertJsonEquals(JSON_FOR_OBJECT_ARRAY, json);
}
@Test
public void testObjectArrayDeserialization() throws Exception {
Object[] objectArr = marshaller.unmarshal(JSON_FOR_OBJECT_ARRAY, Object[].class);
assertThat(objectArr[0], is(instanceOf(Cat.class)));
assertThat(objectArr[1], is(instanceOf(Bird.class)));
assertThat(objectArr[2], is(instanceOf(Person.class)));
assertThat(((Person) objectArr[2]).getName(), is("Jim"));
}
@Test
public void testListSerialization() throws Exception {
List<Animal> animalList = Arrays.asList(felix, polly, felix);
json = marshaller.marshal(animalList);
assertJsonEquals(JSON_FOR_ANIMAL_LIST, json);
}
@Test
public void testListDeserialization() throws Exception {
@SuppressWarnings({ "unchecked" })
List<Animal> animalList = marshaller.unmarshal(JSON_FOR_ANIMAL_LIST, List.class);
assertThat(animalList.get(0), is(instanceOf(Cat.class)));
assertThat(animalList.get(1), is(instanceOf(Bird.class)));
assertThat(animalList.get(2), is(instanceOf(Cat.class)));
assertThat(animalList.get(0).getName(), is("Felix"));
}
@Test
public void testObjectListSerialization() throws Exception {
List<Object> objectList = Arrays.asList(felix, polly, jim);
json = marshaller.marshal(objectList);
assertJsonEquals(JSON_FOR_OBJECT_LIST, json);
}
@Test
public void testObjectListDeserialization() throws Exception {
@SuppressWarnings({ "unchecked" })
List<Object> objectList = marshaller.unmarshal(JSON_FOR_OBJECT_LIST, List.class);
assertThat(objectList.get(0), is(instanceOf(Cat.class)));
assertThat(objectList.get(1), is(instanceOf(Bird.class)));
assertThat(objectList.get(2), is(instanceOf(Person.class)));
assertThat(((Person) objectList.get(2)).getName(), is("Jim"));
}
@Test
public void testSetSerialization() throws Exception { // also relies on deserialization
Set<Object> originalSet = new HashSet<>(Arrays.asList(felix, jim, jim));
json = marshaller.marshal(originalSet);
@SuppressWarnings("unchecked")
Set<Object> deserializedSet = marshaller.unmarshal(json, Set.class);
assertEquals(deserializedSet, originalSet);
}
@Test
public void testSetDeserialization() throws Exception {
@SuppressWarnings({ "unchecked" })
Set<Object> objectSet = marshaller.unmarshal(JSON_FOR_OBJECT_SET, Set.class);
assertThat(objectSet.size(), is(equalTo(2)));
}
@Test
public void testMapSerialization() throws Exception {
Map<String, Object> map = new HashMap<>();
map.put(jim.getName(), jim);
map.put(john.getName(), john);
map.put(felix.getName(), felix);
map.put(polly.getName(), polly);
json = marshaller.marshal(map);
assertJsonEquals(JSON_FOR_OBJECT_MAP, json);
}
@Test
public void testMapDeserialization() throws Exception {
Object object = marshaller.unmarshal(JSON_FOR_OBJECT_MAP, Map.class);
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) object;
assertThat(map.size(), is(equalTo(4)));
assertThat(map.get(jim.getName()), is(equalTo(jim)));
assertThat(map.get(john.getName()), is(equalTo(john)));
assertThat(map.get(felix.getName()), is(equalTo(felix)));
assertThat(map.get(polly.getName()), is(equalTo(polly)));
}
@Test
public void testGivenStaticallyDefinedObjectThenMarshallingWorksWithoutTypeInfo() throws Exception {
json = marshaller.marshal(felix, false);
assertJsonEquals(json, JSON_FOR_STATIC_OBJECT_WITHOUT_TYPE_INFO);
}
@Test
public void testGivenStaticallyDefinedNestedAttributeThenMarshallingWorksWithoutTypeInfo() throws Exception {
CatWrapper catWrapper = new CatWrapper();
catWrapper.setCat(felix);
json = marshaller.marshal(catWrapper, false);
assertJsonEquals(json, JSON_FOR_NESTED_STATIC_ATTRIBUTE_WITHOUT_TYPE_INFO);
}
@Test
public void testProjectBeanSerialization() throws Exception {
ProjectBean bean = marshaller.unmarshal(JSON_FOR_PROJECT_BEAN, ProjectBean.class);
json = marshaller.marshal(bean);
// New json is different from original because it has type info
assertJsonEquals(JSON_FOR_PROJECT_BEAN_WITH_TYPES, json);
}
@Test
public void testProjectBeanSerializationWithoutTypeInfo() throws Exception {
ProjectBean bean = marshaller.unmarshal(JSON_FOR_PROJECT_BEAN, ProjectBean.class);
json = marshaller.marshal(bean, false);
// New json is different from original because it does not include nulls.
// Therefore, test unmarshalled beans instead.
ProjectBean returnBean = marshaller.unmarshal(json, ProjectBean.class);
assertEquals(returnBean, bean);
}
@Test
public void testProjectBeanDeserialization() throws Exception {
marshaller.unmarshal(JSON_FOR_PROJECT_BEAN, ProjectBean.class);
}
@Test
public void testProjectBeanDeserializationWithTypesAsNull() throws Exception {
marshaller.unmarshal(JSON_FOR_PROJECT_BEAN_WITH_TYPES, null);
}
@Test(expected = JsonMappingException.class)
public void testProjectBeanDeserializationWithWrongType() throws Exception {
marshaller.unmarshal(JSON_FOR_PROJECT_BEAN, TestStatusBean.class);
}
// The tests below work with a non-generic wrapper object. This is often not possible with generic
// wrappers (as above) due to type erasure.
@Test
public void testContainedArraySerialization() throws Exception {
ContainerBean container = new ContainerBean();
Object[] objectArr = new Object[] { felix, polly, jim };
container.setObjArray(objectArr);
json = marshaller.marshal(container);
assertJsonEquals(JSON_FOR_CONTAINED_OBJECT_ARRAY, json);
}
@Test
public void testContainedArrayDeserialization() throws Exception {
ContainerBean container = marshaller.unmarshal(JSON_FOR_CONTAINED_OBJECT_ARRAY, null);
Object[] objectArr = container.getObjArray();
assertThat(objectArr[0], is(instanceOf(Cat.class)));
assertThat(objectArr[1], is(instanceOf(Bird.class)));
assertThat(objectArr[2], is(instanceOf(Person.class)));
assertThat(((Person) objectArr[2]).getName(), is("Jim"));
}
@Test
public void testContainedListSerialization() throws Exception {
ContainerBean container = new ContainerBean();
List<Object> objectList = Arrays.asList(felix, polly, jim);
container.setObjList(objectList);
json = marshaller.marshal(container);
assertJsonEquals(JSON_FOR_CONTAINED_OBJECT_LIST, json);
}
@Test
public void testContainedListDeserialization() throws Exception {
ContainerBean container = marshaller.unmarshal(JSON_FOR_CONTAINED_OBJECT_LIST, null);
List<Object> objectList = (List<Object>) container.getObjList();
assertThat(objectList.get(0), is(instanceOf(Cat.class)));
assertThat(objectList.get(1), is(instanceOf(Bird.class)));
assertThat(objectList.get(2), is(instanceOf(Person.class)));
assertThat(((Person) objectList.get(2)).getName(), is("Jim"));
}
@Test
public void testContainedMapSerialization() throws Exception {
ContainerBean container = new ContainerBean();
Map<String, Object> map = new HashMap<>();
map.put(jim.getName(), jim);
map.put(john.getName(), john);
map.put(felix.getName(), felix);
map.put(polly.getName(), polly);
container.setObjMap(map);
json = marshaller.marshal(container);
assertJsonEquals(JSON_FOR_CONTAINED_OBJECT_MAP, json);
}
@Test
public void testContainedMapDeserialization() throws Exception {
ContainerBean container = marshaller.unmarshal(JSON_FOR_CONTAINED_OBJECT_MAP, null);
Map<String, Object> objectMap = container.getObjMap();
assertThat(objectMap.size(), is(equalTo(4)));
assertThat(objectMap.get(jim.getName()), is(equalTo(jim)));
assertThat(objectMap.get(john.getName()), is(equalTo(john)));
assertThat(objectMap.get(felix.getName()), is(equalTo(felix)));
assertThat(objectMap.get(polly.getName()), is(equalTo(polly)));
}
@Rule public ExpectedException exception = ExpectedException.none();
@SuppressWarnings("unused")
@Test
public void testGivenClassIdNotRecognisedWhenUnmarshallingWithNullThenExceptionRaisedWithCorrectMessage() throws Exception {
exception.expect(IllegalArgumentException.class);
exception.expectMessage(CoreMatchers.containsString("jsontest.doesnotexist"));
Object object;
object = marshaller.unmarshal(JSON_FOR_UNKNOWN_TOP_LEVEL_CLASS_ID, null);
}
@SuppressWarnings("unused")
@Test
public void testGivenClassIdNotPresentWhenUnmarshallingWithNullThenExceptionRaised() throws Exception {
exception.expect(JsonMappingException.class);
exception.expectMessage(CoreMatchers.containsString("type id"));
Object object;
object = marshaller.unmarshal(JSON_FOR_NO_TOP_LEVEL_CLASS_ID, null);
}
@SuppressWarnings("unused")
@Test
public void testGivenNestedClassIdNotRecognisedWhenUnmarshallingDynamicDefinedObjectThenExceptionRaisedWithCorrectMessage() throws Exception {
exception.expect(JsonMappingException.class);
exception.expectMessage(CoreMatchers.containsString("jsontest.nonexistent"));
Object object;
object = marshaller.unmarshal(JSON_FOR_UNKNOWN_NESTED_DYNAMIC_DEFINED_CLASS_ID, null);
}
@SuppressWarnings("unused")
@Test
public void testGivenNestedClassIdNotPresentWhenUnmarshallingDynamicDefinedObjectThenExceptionRaisedWithCorrectMessage() throws Exception {
exception.expect(JsonMappingException.class);
exception.expectMessage(CoreMatchers.containsString("type id"));
Object object;
object = marshaller.unmarshal(JSON_FOR_NESTED_DYNAMIC_DEFINED_CLASS_WITH_NO_ID, null);
}
@Test
public void testGivenClassIdNotRecognisedWhenUnmarshallingDynamicDefinedObjectThenMarshalSuccessful() throws Exception {
TestTypeNonRegisteredImpl object;
object = marshaller.unmarshal(JSON_FOR_UNKNOWN_TOP_LEVEL_CLASS_ID, TestTypeNonRegisteredImpl.class);
assertEquals(object.getString(), "Non-registered, top level class.");
}
@Test
public void testGivenNestedClassIdNotRecognisedWhenUnmarshallingStaticDefinedObjectThenMarshalSuccessful() throws Exception {
CatWrapper catWrapper;
catWrapper = marshallerWithNoRegistry.unmarshal(JSON_FOR_UNKNOWN_NESTED_STATIC_DEFINED_CLASS_ID, CatWrapper.class);
assertEquals(catWrapper.getCat().getName(), "Tiddles");
assertEquals(catWrapper.getCat().getWhiskers(), "Luxuriant");
}
@Test
public void testGivenNestedClassIdNotPresentWhenUnmarshallingStaticDefinedObjectThenMarshalSuccessful() throws Exception {
CatWrapper catWrapper;
catWrapper = marshallerWithNoRegistry.unmarshal(JSON_FOR_STATIC_DEFINED_CLASS_WITH_NO_ID, CatWrapper.class);
assertEquals(catWrapper.getCat().getName(), "Tiddles");
assertEquals(catWrapper.getCat().getWhiskers(), "Luxuriant");
}
@Test
public void testGivenClassNotRecognisedWhenMarshallingUnknownObjectThenExceptionRaisedWithCorrectMessage() throws Exception {
exception.expect(JsonMappingException.class);
exception.expectMessage(CoreMatchers.containsString(Bird.class.toString()));
json = marshallerWithNoRegistry.marshal(jim);
}
}