// Copyright (c) 2003-present, Jodd Team (http://jodd.org) // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. package jodd.json; import jodd.json.impl.DateJsonSerializer; import jodd.json.fixtures.mock.Employee; import jodd.json.fixtures.mock.Group; import jodd.json.fixtures.mock.Network; import jodd.json.fixtures.mock.Pair; import jodd.json.fixtures.mock.Person; import jodd.json.fixtures.mock.Phone; import jodd.json.fixtures.mock.Spiderman; import jodd.json.fixtures.mock.superhero.Hero; import jodd.json.fixtures.mock.superhero.SecretIdentity; import jodd.json.fixtures.mock.superhero.SecretLair; import jodd.json.fixtures.mock.superhero.SuperPower; import jodd.json.fixtures.mock.superhero.Villian; import jodd.json.fixtures.mock.superhero.XRayVision; import jodd.json.fixtures.model.Account; import jodd.util.StringUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.awt.geom.Point2D; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TimeZone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; public class JSONDeserializerTest { private static final double DELTA = 0.000000001; private DataCreator creator; @Before public void setUp() { creator = new DataCreator(); } @After public void tearDown() { JoddJson.classMetadataName = null; } @Test public void testDeserializeNoIncludes() { Person jodder = creator.createJodder(); String json = new JsonSerializer().serialize(jodder); Person jsonJodder = new JsonParser().parse(json, Person.class); assertNotNull(jsonJodder); assertEquals(jodder.getLastname(), jsonJodder.getLastname()); assertEquals(jodder.getFirstname(), jsonJodder.getFirstname()); assertEquals(jodder.getBirthdate(), jsonJodder.getBirthdate()); assertEquals(jodder.getHome().getState(), jsonJodder.getHome().getState()); assertEquals(jodder.getHome().getStreet(), jsonJodder.getHome().getStreet()); assertEquals(jodder.getHome().getCity(), jsonJodder.getHome().getCity()); assertEquals(jodder.getWork().getCity(), jsonJodder.getWork().getCity()); assertEquals(jsonJodder, jsonJodder.getWork().getPerson()); assertEquals(0, jsonJodder.getHobbies().size()); } @Test public void testDeserializeWithPath() { Person igor = creator.createJodder(); Map map = new HashMap(); map.put("person", igor); String json = new JsonSerializer().serialize(map); map = new JsonParser().map("values", Person.class).parse(json); Person jsonIgor = (Person) map.get("person"); assertNotNull(jsonIgor); assertEquals(igor.getLastname(), jsonIgor.getLastname()); assertEquals(igor.getFirstname(), jsonIgor.getFirstname()); assertEquals(igor.getBirthdate(), jsonIgor.getBirthdate()); assertEquals(igor.getHome().getState(), jsonIgor.getHome().getState()); assertEquals(igor.getHome().getStreet(), jsonIgor.getHome().getStreet()); assertEquals(igor.getHome().getCity(), jsonIgor.getHome().getCity()); assertEquals(igor.getWork().getCity(), jsonIgor.getWork().getCity()); assertEquals(jsonIgor, jsonIgor.getWork().getPerson()); } @Test public void testDeserializeWithIncludes() { Person igor = creator.createJodder(); String json = new JsonSerializer().include("phones", "hobbies").serialize(igor); Person jsonIgor = new JsonParser().parse(json, Person.class); assertEquals(2, jsonIgor.getPhones().size()); assertEquals(0, jsonIgor.getHobbies().size()); } @Test public void testSubClassDeserialize() { Employee dilbert = creator.createDilbert(); String json = new JsonSerializer().include("phones", "hobbies").serialize(dilbert); Person jsonDilbert = new JsonParser().parse(json, Employee.class); assertNotNull(jsonDilbert); assertTrue(jsonDilbert instanceof Employee); assertEquals(dilbert.getCompany(), ((Employee) jsonDilbert).getCompany()); } @Test public void testDeserializeInterfaces() { Hero superman = creator.createSuperman(); String json = new JsonSerializer().include("powers").setClassMetadataName("class").serialize(superman); Hero jsonSuperMan = new JsonParser().setClassMetadataName("class").parse(json, Hero.class); assertNotNull(jsonSuperMan); assertEquals(4, jsonSuperMan.getPowers().size()); assertHeroHasSuperPowers(jsonSuperMan); JoddJson.classMetadataName = null; } @Test public void testDeserializeInterfaces2() { Hero superman = creator.createSuperman(); String json = new JsonSerializer().include("powers").withClassMetadata(true).serialize(superman); Hero jsonSuperMan = new JsonParser().withClassMetadata(true).parse(json, Hero.class); assertNotNull(jsonSuperMan); assertEquals(4, jsonSuperMan.getPowers().size()); assertHeroHasSuperPowers(jsonSuperMan); JoddJson.classMetadataName = null; } @Test public void testNoClassHints() { JoddJson.classMetadataName = null; Hero superman = creator.createSuperman(); String json = new JsonSerializer().exclude("*.class").serialize(superman); Hero jsonSuperMan = new JsonParser() .map(Hero.class) .map("lair", SecretLair.class) .map("secretIdentity", SecretIdentity.class) .parse(json); assertNotNull(jsonSuperMan); assertEquals("Super Man", jsonSuperMan.getName()); assertNotNull(jsonSuperMan.getIdentity()); assertEquals("Clark Kent", jsonSuperMan.getIdentity().getName()); assertNotNull(jsonSuperMan.getLair()); assertEquals("Fortress of Solitude", jsonSuperMan.getLair().getName()); } @Test public void testNoHintsButClassesForCollection() { JoddJson.classMetadataName = "class"; Hero superman = creator.createSuperman(); String json = new JsonSerializer() .exclude("*.class") .include("powers.class") .serialize(superman); Hero jsonSuperMan = new JsonParser().parse(json, Hero.class); assertHeroHasSuperPowers(jsonSuperMan); } @Test public void testNoClassHintsForCollections() { JoddJson.classMetadataName = "class"; Hero superman = creator.createSuperman(); String json = new JsonSerializer() .include("powers") // redudant .include("powers.class") .withSerializer("powers.class", new SimpleClassnameTransformer()) .exclude("*.class") .serialize(superman); int count = StringUtil.count(json, "***"); assertEquals(4, count); json = StringUtil.remove(json, "***"); Hero jsonSuperMan = new JsonParser() .map("lair", SecretLair.class) .map("secretIdentity", SecretIdentity.class) .parse(json, Hero.class); assertEquals("Fortress of Solitude", jsonSuperMan.getLair().getName()); assertHeroHasSuperPowers(jsonSuperMan); } @Test public void testListSerialization() { JoddJson.classMetadataName = "class"; Person modesty = creator.createModesty(); Person igor = creator.createJodder(); Person pedro = creator.createPedro(); List<Person> list = new ArrayList<>(3); list.add(modesty); list.add(igor); list.add(pedro); String json = new JsonSerializer().serialize(list); List<Person> people = new JsonParser().parse(json); assertEquals(ArrayList.class, people.getClass()); json = new JsonSerializer().exclude("*.class").serialize(list); people = new JsonParser().map("values", Person.class).parse(json); assertEquals(ArrayList.class, people.getClass()); assertEquals(3, people.size()); assertEquals(Person.class, people.get(0).getClass()); List<Map> peopleMap = new JsonParser().parse(json); assertEquals(ArrayList.class, peopleMap.getClass()); assertEquals(3, peopleMap.size()); assertEquals(HashMap.class, peopleMap.get(0).getClass()); } @Test public void testGenericTypeDeserialization() { JoddJson.classMetadataName = "class"; Pair<Hero, Villian> archenemies = new Pair<>(creator.createSuperman(), creator.createLexLuthor()); String json = new JsonSerializer() .exclude("*.class") .serialize(archenemies); Pair<Hero, Villian> deserialArchEnemies = new JsonParser() .map("first", Hero.class) .map("second", Villian.class) .parse(json, Pair.class); assertEquals(archenemies.getFirst().getClass(), deserialArchEnemies.getFirst().getClass()); assertEquals(archenemies.getSecond().getClass(), deserialArchEnemies.getSecond().getClass()); assertEquals(archenemies.getFirst().getIdentity(), deserialArchEnemies.getFirst().getIdentity()); assertEquals(archenemies.getFirst().getLair(), deserialArchEnemies.getFirst().getLair()); assertEquals(archenemies.getFirst().getName(), deserialArchEnemies.getFirst().getName()); assertEquals(archenemies.getSecond().getName(), deserialArchEnemies.getSecond().getName()); assertEquals(archenemies.getSecond().getLair(), deserialArchEnemies.getSecond().getLair()); } @Test public void testGenericTypeDeserialization2() { Pair<Hero, Villian> archenemies = new Pair<>(creator.createSuperman(), creator.createLexLuthor()); String json = new JsonSerializer() .serialize(archenemies); Pair<Hero, Villian> deserialArchEnemies = new JsonParser() .map("first", Hero.class) .map("second", Villian.class) .parse(json, Pair.class); assertEquals(archenemies.getFirst().getClass(), deserialArchEnemies.getFirst().getClass()); assertEquals(archenemies.getSecond().getClass(), deserialArchEnemies.getSecond().getClass()); assertEquals(archenemies.getFirst().getIdentity(), deserialArchEnemies.getFirst().getIdentity()); assertEquals(archenemies.getFirst().getLair(), deserialArchEnemies.getFirst().getLair()); assertEquals(archenemies.getFirst().getName(), deserialArchEnemies.getFirst().getName()); assertEquals(archenemies.getSecond().getName(), deserialArchEnemies.getSecond().getName()); assertEquals(archenemies.getSecond().getLair(), deserialArchEnemies.getSecond().getLair()); } @Test public void testGeneralMapDeserialization() { JoddJson.classMetadataName = "class"; String json = new JsonSerializer().exclude("*.class").serialize(creator.createJodder()); Map<String, Object> deserialized = new JsonParser().parse(json); assertEquals("Igor", deserialized.get("firstname")); assertEquals("Spasic", deserialized.get("lastname")); assertTrue(Map.class.isAssignableFrom(deserialized.get("work").getClass())); assertTrue(Map.class.isAssignableFrom(deserialized.get("home").getClass())); } @Test public void testGeneralMapDeserialization2() { String json = new JsonSerializer().serialize(creator.createJodder()); Map<String, Object> deserialized = new JsonParser().parse(json); assertEquals("Igor", deserialized.get("firstname")); assertEquals("Spasic", deserialized.get("lastname")); assertTrue(Map.class.isAssignableFrom(deserialized.get("work").getClass())); assertTrue(Map.class.isAssignableFrom(deserialized.get("home").getClass())); } @Test public void testListDeserializationNoClass() { JoddJson.classMetadataName = "class"; Person modesty = creator.createModesty(); Person igor = creator.createJodder(); Person pedro = creator.createPedro(); List<Person> list = new ArrayList<>(3); list.add(modesty); list.add(igor); list.add(pedro); String json = new JsonSerializer().exclude("*.class").serialize(list); List<Person> people = new JsonParser() .map("values", Person.class).parse(json); assertEquals(ArrayList.class, people.getClass()); assertEquals(3, list.size()); assertEquals(modesty.getFirstname(), list.get(0).getFirstname()); assertEquals(igor.getFirstname(), list.get(1).getFirstname()); assertEquals(pedro.getFirstname(), list.get(2).getFirstname()); } @Test public void testListDeserializationNoClass2() { Person modesty = creator.createModesty(); Person igor = creator.createJodder(); Person pedro = creator.createPedro(); List<Person> list = new ArrayList<>(3); list.add(modesty); list.add(igor); list.add(pedro); String json = new JsonSerializer().serialize(list); List<Person> people = new JsonParser() .map("values", Person.class).parse(json); assertEquals(ArrayList.class, people.getClass()); assertEquals(3, list.size()); assertEquals(modesty.getFirstname(), list.get(0).getFirstname()); assertEquals(igor.getFirstname(), list.get(1).getFirstname()); assertEquals(pedro.getFirstname(), list.get(2).getFirstname()); } @Test public void testDateTransforming() throws ParseException { final SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd"); Person foo = new Person("Foo", "Bar", new Date(), null, null); foo.setBirthdate(df.parse("2009/01/02")); String json = new JsonSerializer().withSerializer("birthdate", new DateJsonSerializer() { @Override public boolean serialize(JsonContext jsonContext, Date date) { jsonContext.writeString(df.format(date)); return true; } }).serialize(foo); Person newUser = new JsonParser() .withValueConverter("birthdate", new ValueConverter<String, Date>() { public Date convert(String data) { try { return df.parse(data); } catch (ParseException pe) { throw new JsonException(pe); } } }) .parse(json, Person.class); assertEquals(foo.getBirthdate(), newUser.getBirthdate()); assertEquals("2009/01/02", df.format(newUser.getBirthdate())); } @Test public void testMapWithEmbeddedObject() { Map<String, Network> networks = new JsonParser() .setClassMetadataName("class") .parse("{\"1\": {\"class\":\"" + Network.class.getName() + "\", \"name\": \"Jodd\"} }"); assertNotNull(networks); assertEquals(1, networks.size()); assertTrue(networks.containsKey("1")); assertNotNull(networks.get("1")); assertEquals(Network.class, networks.get("1").getClass()); assertEquals("Jodd", networks.get("1").getName()); } @Test public void testMapWithEmbeddedObject2() { JoddJson.classMetadataName = null; Map<String, Pair<Phone, Network>> complex = new JsonParser() .map("values", Pair.class) .map("values.first", Phone.class) .map("values.second", Network.class) .parse("{\"1\": { \"first\": { \"areaCode\": \"404\" }, \"second\": {\"name\": \"Jodd\"} } }"); assertNotNull(complex); assertEquals(1, complex.size()); assertTrue(complex.containsKey("1")); assertNotNull(complex.get("1")); assertEquals(Pair.class, complex.get("1").getClass()); assertEquals(Phone.class, complex.get("1").getFirst().getClass()); assertEquals(Network.class, complex.get("1").getSecond().getClass()); assertEquals("404", complex.get("1").getFirst().getAreaCode()); assertEquals("Jodd", complex.get("1").getSecond().getName()); } @Test public void testListWithEmbeddedObject() { List<Network> networks = new JsonParser() .setClassMetadataName("class") .parse("[" + " {\"class\":\"" + Network.class.getName() + "\", \"name\": \"Jodd\"}," + " {\"class\":\"" + Network.class.getName() + "\", \"name\": \"Mojo\"}" + "]"); assertNotNull(networks); assertEquals(2, networks.size()); Network network = networks.get(0); assertEquals("Jodd", network.getName()); network = networks.get(1); assertEquals("Mojo", network.getName()); } @Test public void testArrayType() { Person igor = creator.createJodder(); Person modesty = creator.createModesty(); Group group = new Group("brothers", igor, modesty); String json = new JsonSerializer().include("people").exclude("*.class").serialize(group); Group bro = new JsonParser().map(Group.class).parse(json); assertNotNull(bro); assertEquals("brothers", bro.getGroupName()); assertEquals(2, bro.getPeople().length); assertEquals("Igor", bro.getPeople()[0].getFirstname()); assertEquals("Modesty", bro.getPeople()[1].getFirstname()); } @Test public void testEmptyArray() { Group group = new JsonParser().parse("{\"people\": [], \"groupName\": \"Nobody\" }", Group.class); assertEquals("Nobody", group.getGroupName()); assertEquals(0, group.getPeople().length); } @Test public void testNullDeserialization() { String input = "{\"property\": null, \"property2\":5, \"property3\":\"abc\"}"; JsonParser deserializer = new JsonParser(); deserializer.map(null, HashMap.class); Map<String, Object> result = deserializer.parse(input); assertNotNull(result); // fails on this line, because the first property is not deserialized assertEquals(3, result.size()); assertTrue(result.containsKey("property")); assertNull("the value should be null", result.get("property")); } @Test public void testPrimitives() { List<Date> dates = new ArrayList<>(); dates.add(new Date()); dates.add(new Date(1970, 1, 12)); dates.add(new Date(1986, 3, 21)); String json = new JsonSerializer().serialize(dates); List<Date> jsonDates = new JsonParser() .map(null, ArrayList.class) .map("values", Date.class) .parse(json); assertEquals(jsonDates.size(), dates.size()); assertEquals(Date.class, jsonDates.get(0).getClass()); List<? extends Number> numbers = Arrays.asList(1, 0.5, 100.4f, (short) 5); json = new JsonSerializer().serialize(numbers); List<Number> jsonNumbers = new JsonParser().parse(json); assertEquals(numbers.size(), jsonNumbers.size()); for (int i = 0; i < numbers.size(); i++) { assertEquals(numbers.get(i).floatValue(), jsonNumbers.get(i).floatValue(), DELTA); } List<Boolean> bools = Arrays.asList(true, false, true, false, false); json = new JsonSerializer().serialize(bools); List<Boolean> jsonBools = new JsonParser().parse(json); assertEquals(bools.size(), jsonBools.size()); for (int i = 0; i < bools.size(); i++) { assertEquals(bools.get(i), jsonBools.get(i)); } assertEquals(numbers.size(), jsonNumbers.size()); } @Test public void testArray() { Person[] p = new Person[3]; p[0] = creator.createJodder(); p[1] = creator.createDilbert(); p[2] = creator.createModesty(); String json = new JsonSerializer().serialize(p); Person[] jsonP = new JsonParser().parse(json, Person[].class); assertEquals(3, jsonP.length); assertEquals("Igor", jsonP[0].getFirstname()); assertEquals("Dilbert", jsonP[1].getFirstname()); assertEquals("Modesty", jsonP[2].getFirstname()); } @Test public void testDeserializationIntoPublicFields() { JoddJson.classMetadataName = "class"; Spiderman spiderman = new Spiderman(); spiderman.spideySense = false; spiderman.superpower = "Creates Many Webs and Super Tough"; String json = new JsonSerializer().serialize(spiderman); Spiderman jsonSpiderman = new JsonParser().parse(json); assertEquals(spiderman.spideySense, jsonSpiderman.spideySense); assertEquals(spiderman.superpower, jsonSpiderman.superpower); } @Test public void testAutoTypeConvertToNumerical() { Account account = new JsonParser() .parse("{\"id\": \"5\", \"accountNumber\": \"1234567-123\"}", Account.class); assertEquals(new Integer(5), account.getId()); XRayVision xray = new JsonParser().parse("{ \"power\": \"2.3\" }", XRayVision.class); assertEquals(2.3f, xray.getPower(), DELTA); } @Test public void testDeserializeURL() { String json = "{\n" + " \"oslc_cm:next\": \"http:\\/\\/localhost:9080\\/results\\/3\",\n" + " \"oslc_cm:previous\": \"http:\\/\\/localhost:9080\\/results\\/1\", \n" + " \"oslc_cm:totalCount\" : 27,\n" + " \"oslc_cm:results\": [\n" + " {\n" + " \"rdf:resource\": \"http:\\/\\/localhost:9080\\/records\\/1234\"\n" + " },\n" + " {\n" + " \"rdf:resource\": \"http:\\/\\/localhost:9080\\/records\\/1235\"\n" + " },\n" + " {\n" + " \"rdf:resource\": \"http:\\/\\/localhost:9080\\/records\\/1236\"\n" + " } \n" + " ]\n" + "}"; Map<String, Object> page2 = new JsonParser().parse(json); assertEquals("http://localhost:9080/results/3", page2.get("oslc_cm:next")); assertEquals(3, ((List) page2.get("oslc_cm:results")).size()); } @Test public void testPoint() { JoddJson.classMetadataName = "__class"; String json = new JsonSerializer().serialize(new Point2D.Float(1.0f, 2.0f)); Point2D.Float point = new JsonParser().parse(json); assertEquals(1.0f, point.x, DELTA); assertEquals(2.0f, point.y, DELTA); } @Test public void testUnixEpoch() { Calendar cal = Calendar.getInstance(); cal.setTimeZone(TimeZone.getTimeZone("GMT")); cal.set(Calendar.YEAR, 1970); cal.set(Calendar.MONTH, 0); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.AM_PM, Calendar.AM); cal.set(Calendar.HOUR, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); Person hank = new Person("Hank", "Paulsen", cal.getTime(), null, null); String json = new JsonSerializer().serialize(hank); Person deHank = new JsonParser().parse(json, Person.class); assertEquals(hank.getFirstname(), deHank.getFirstname()); assertEquals(hank.getLastname(), deHank.getLastname()); assertEquals(hank.getBirthdate(), deHank.getBirthdate()); } public static class SimpleClassnameTransformer implements TypeJsonSerializer { public boolean serialize(JsonContext jsonContext, Object value) { String name = value.toString() + "***"; jsonContext.writeString(name); return true; } } private void assertHeroHasSuperPowers(Hero hero) { for (int i = 0; i < hero.getPowers().size(); i++) { assertTrue(hero.getPowers().get(i) instanceof SuperPower); } } }