// 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.ObjectJsonSerializer; import jodd.json.fixtures.mock.Address; import jodd.json.fixtures.mock.Employee; import jodd.json.fixtures.mock.Friend; import jodd.json.fixtures.mock.Hill; import jodd.json.fixtures.mock.Mountain; import jodd.json.fixtures.mock.Network; import jodd.json.fixtures.mock.Person; import jodd.json.fixtures.mock.Phone; import jodd.json.fixtures.mock.Spiderman; import jodd.json.fixtures.mock.Surfer; import jodd.json.fixtures.mock.TestClass2; import jodd.json.fixtures.mock.TestClass3; import jodd.json.fixtures.mock.Zipcode; import jodd.json.fixtures.model.ListContainer; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class JSONSerializationTest { private Person jodder; private Person modesty; private Map colors; private List people; private Network network; private Zipcode pedroZip; private Employee dilbert; @Before @SuppressWarnings({"unchecked"}) public void setUp() { DataCreator dataCreator = new DataCreator(); pedroZip = new Zipcode("848485"); Person pedro = dataCreator.createPedro(); jodder = dataCreator.createJodder(); modesty = dataCreator.createModesty(); colors = dataCreator.createColorMap(); people = new ArrayList(); people.add(jodder); people.add(modesty); people.add(pedro); dilbert = dataCreator.createDilbert(); network = dataCreator.createNetwork("My Network", jodder, modesty); } @After public void tearDown() { JoddJson.classMetadataName = null; } @Test public void testObject() { JoddJson.classMetadataName = "class"; JsonSerializer serializer = new JsonSerializer(); String jodderJson = serializer.serialize(jodder); assertStringValue(Person.class.getName(), jodderJson); assertAttribute("firstname", jodderJson); assertStringValue("Igor", jodderJson); assertAttribute("lastname", jodderJson); assertStringValue("Spasic", jodderJson); assertAttribute("work", jodderJson); assertAttribute("home", jodderJson); assertAttribute("street", jodderJson); assertStringValue(Address.class.getName(), jodderJson); assertAttribute("zipcode", jodderJson); assertStringValue(Zipcode.class.getName(), jodderJson); assertAttributeMissing("person", jodderJson); assertAttributeMissing("phones", jodderJson); assertStringValueMissing(Phone.class.getName(), jodderJson); assertAttributeMissing("hobbies", jodderJson); JsonSerializer jdrSerializer = new JsonSerializer(); jdrSerializer.exclude("home", "work"); String modestyJson = jdrSerializer.serialize(modesty); assertStringValue(Person.class.getName(), modestyJson); assertAttribute("firstname", modestyJson); assertStringValue("Modesty", modestyJson); assertAttribute("lastname", modestyJson); assertStringValue("Blase", modestyJson); assertAttribute("birthdate", modestyJson); assertStringValueMissing(Address.class.getName(), modestyJson); assertAttributeMissing("work", modestyJson); assertAttributeMissing("home", modestyJson); assertAttributeMissing("street", modestyJson); assertAttributeMissing("city", modestyJson); assertAttributeMissing("state", modestyJson); assertStringValueMissing(Zipcode.class.getName(), modestyJson); assertAttributeMissing("zipcode", modestyJson); assertStringValueMissing(Phone.class.getName(), modestyJson); assertAttributeMissing("hobbies", modestyJson); assertAttributeMissing("person", modestyJson); serializer.exclude("home.zipcode", "work.zipcode"); String json2 = serializer.serialize(jodder); assertStringValue(Person.class.getName(), json2); assertAttribute("work", json2); assertAttribute("home", json2); assertAttribute("street", json2); assertStringValue(Address.class.getName(), json2); assertAttributeMissing("zipcode", json2); assertAttributeMissing("phones", json2); assertStringValueMissing(Zipcode.class.getName(), json2); assertStringValueMissing(Phone.class.getName(), json2); assertAttributeMissing("hobbies", json2); assertAttributeMissing("type", json2); assertStringValueMissing("PAGER", json2); serializer.include("hobbies").exclude(true, "phones.areaCode", "phones.exchange", "phones.number"); String json3 = serializer.serialize(jodder); assertStringValue(Person.class.getName(), json3); assertAttribute("work", json3); assertAttribute("home", json3); assertAttribute("street", json3); assertStringValue(Address.class.getName(), json3); assertAttribute("phones", json3); assertAttribute("phoneNumber", json3); assertStringValue(Phone.class.getName(), json3); assertAttribute("hobbies", json3); assertAttributeMissing("zipcode", json3); assertAttributeMissing(Zipcode.class.getName(), json3); assertAttributeMissing("areaCode", json3); assertAttributeMissing("exchange", json3); assertAttributeMissing("number", json3); assertAttribute("type", json3); assertStringValue("PAGER", json3); assertTrue(json3.startsWith("{")); assertTrue(json3.endsWith("}")); } @Test public void testMap() { JsonSerializer serializer = new JsonSerializer(); String colorsJson = serializer.serialize(colors); for (Object o : colors.entrySet()) { Map.Entry entry = (Map.Entry) o; assertAttribute(entry.getKey().toString(), colorsJson); assertStringValue(entry.getValue().toString(), colorsJson); } assertTrue(colorsJson.startsWith("{")); assertTrue(colorsJson.endsWith("}")); colors.put(null, "#aaaaaa"); colors.put("orange", null); String json = serializer.serialize(colors); assertTrue(json.contains("null:")); assertStringValue("#aaaaaa", json); assertAttribute("orange", json); assertTrue(json.contains(":null")); } @Test public void testArray() { int[] array = new int[30]; for (int i = 0; i < array.length; i++) { array[i] = i; } String json = new JsonSerializer().serialize(array); for (int i = 0; i < array.length; i++) { assertNumber(i, json); } assertFalse(json.contains("\"")); assertFalse(json.contains("\'")); } @Test public void testCollection() { JsonSerializer serializer = new JsonSerializer(); String colorsJson = serializer.serialize(colors.values()); for (Object o : colors.entrySet()) { Map.Entry entry = (Map.Entry) o; assertAttributeMissing(entry.getKey().toString(), colorsJson); assertStringValue(entry.getValue().toString(), colorsJson); } assertTrue(colorsJson.startsWith("[")); assertTrue(colorsJson.endsWith("]")); } @Test public void testString() { assertSerializedTo("Hello", "\"Hello\""); assertSerializedTo("Hello\nWorld", "\"Hello\\nWorld\""); assertSerializedTo("Hello 'Big Boy'", "\"Hello 'Big Boy'\""); assertSerializedTo("Fly \"you fools\"", "\"Fly \\\"you fools\\\"\""); assertSerializedTo("</script>", "\"<\\/script>\""); } @Test public void testListOfObjects() { JoddJson.classMetadataName = "class"; JsonSerializer serializer = new JsonSerializer(); String peopleJson = serializer.serialize(people); assertStringValue(Person.class.getName(), peopleJson); assertAttribute("firstname", peopleJson); assertStringValue("Igor", peopleJson); assertStringValue("Modesty", peopleJson); assertAttribute("lastname", peopleJson); assertStringValue("Spasic", peopleJson); assertStringValue(Address.class.getName(), peopleJson); assertStringValue("Pedro", peopleJson); assertStringValue("Sanchez", peopleJson); serializer = new JsonSerializer().exclude("home", "work"); peopleJson = serializer.serialize(people); assertStringValue(Person.class.getName(), peopleJson); assertAttribute("firstname", peopleJson); assertStringValue("Igor", peopleJson); assertStringValue("Modesty", peopleJson); assertAttribute("lastname", peopleJson); assertStringValue("Spasic", peopleJson); assertStringValueMissing(Address.class.getName(), peopleJson); } @Test public void testDeepIncludes() { JsonSerializer serializer = new JsonSerializer(); String peopleJson = serializer.include("people.hobbies").serialize(network); assertAttribute("name", peopleJson); assertStringValue("My Network", peopleJson); assertAttribute("firstname", peopleJson); assertStringValue("Igor", peopleJson); assertStringValue("Modesty", peopleJson); assertAttribute("lastname", peopleJson); assertStringValue("Spasic", peopleJson); assertAttribute("hobbies", peopleJson); assertStringValue("read", peopleJson); } @Test public void testDates() { JsonSerializer serializer = new JsonSerializer(); String peopleJson = serializer.exclude("home", "work").serialize(jodder); assertAttribute("firstname", peopleJson); assertStringValue("Igor", peopleJson); assertNumber(jodder.getBirthdate().getTime(), peopleJson); assertStringValueMissing("java.util.Date", peopleJson); } @Test public void testSimpleShallowWithListInMap() { JsonSerializer serializer = new JsonSerializer(); Map wrapper = new HashMap(); wrapper.put("name", "Joe Blow"); wrapper.put("people", people); String peopleJson = serializer.serialize(wrapper); assertFalse(peopleJson.contains("[")); serializer.include("people.*"); peopleJson = serializer.serialize(wrapper); assertTrue(peopleJson.contains("[")); } @Test public void testSimpleShallowWithListInObject() { JsonSerializer serializer = new JsonSerializer(); ListContainer wrapper = new ListContainer(); wrapper.setName("Joe Blow"); wrapper.setPeople(people); String peopleJson = serializer.serialize(wrapper); assertFalse(peopleJson.contains("[")); } @Test public void testSetIncludes() { JsonSerializer serializer = new JsonSerializer(); serializer.include("people.hobbies", "phones", "home", "people.resume"); assertEquals(4, serializer.rules.totalRules()); assertEquals("[people.hobbies]", serializer.rules.getRule(0).toString()); assertEquals("[phones]", serializer.rules.getRule(1).toString()); assertEquals("[home]", serializer.rules.getRule(2).toString()); assertEquals("[people.resume]", serializer.rules.getRule(3).toString()); } @Test public void testDeepSerialization() { JsonSerializer serializer = new JsonSerializer(); String peopleJson = serializer.deep(true).serialize(network); assertAttribute("name", peopleJson); assertStringValue("My Network", peopleJson); assertAttribute("firstname", peopleJson); assertStringValue("Igor", peopleJson); assertStringValue("Modesty", peopleJson); assertAttribute("lastname", peopleJson); assertStringValue("Spasic", peopleJson); assertAttributeMissing("hobbies", peopleJson); // annotation explicitly excludes this assertStringValueMissing("read", peopleJson); } @Test public void testDeepSerializationWithIncludeOverrides() { JsonSerializer serializer = new JsonSerializer(); String peopleJson = serializer.include("people.hobbies").deep(true).serialize(network); assertAttribute("firstname", peopleJson); assertStringValue("Igor", peopleJson); assertAttribute("hobbies", peopleJson); assertStringValue("read", peopleJson); assertStringValue("run", peopleJson); assertStringValue("code", peopleJson); } @Test public void testDeepSerializationWithExcludes() { JsonSerializer serializer = new JsonSerializer(); String peopleJson = serializer.exclude("people.work").deep(true).serialize(network); assertAttribute("firstname", peopleJson); assertStringValue("Igor", peopleJson); assertAttributeMissing("work", peopleJson); assertStringValue("173 Hackers Drive", peopleJson); assertAttribute("home", peopleJson); assertAttribute("phones", peopleJson); } @Test public void testDeepSerializationCycles() { JsonSerializer serializer = new JsonSerializer(); String json = serializer.deep(true).serialize(people); assertAttribute("zipcode", json); assertEquals(2, occurs(pedroZip.getZipcode(), json)); assertAttributeMissing("person", json); } @Test public void testSerializeSuperClass() { JsonSerializer serializer = new JsonSerializer(); String json = serializer.serialize(dilbert); assertAttribute("company", json); assertStringValue("Initech", json); assertAttribute("firstname", json); assertStringValue("Dilbert", json); } @Test public void testSerializePublicFields() { Spiderman spiderman = new Spiderman(); JsonSerializer serializer = new JsonSerializer(); String json = serializer.serialize(spiderman); assertAttribute("spideySense", json); assertAttribute("superpower", json); assertStringValue("Creates web", json); } @Test public void testExcludingPublicFields() { Spiderman spiderman = new Spiderman(); String json = new JsonSerializer().exclude("superpower").serialize(spiderman); assertAttributeMissing("superpower", json); assertAttribute("spideySense", json); } @Test public void testWildcards() { JoddJson.classMetadataName = "class"; JsonSerializer serializer = new JsonSerializer(); String json = serializer.include("phones").exclude("*.class").serialize(jodder); assertAttributeMissing("class", json); assertAttribute("phones", json); assertAttributeMissing("hobbies", json); } @Test public void testExclude() { String json = new JsonSerializer().serialize(jodder); assertAttribute("firstname", json); assertAttributeMissing("number", json); assertAttributeMissing("exchange", json); assertAttributeMissing("areaCode", json); json = new JsonSerializer().include("phones").serialize(jodder); assertAttribute("firstname", json); assertAttribute("number", json); assertAttribute("exchange", json); assertAttribute("areaCode", json); json = new JsonSerializer().include("phones").exclude("phones.areaCode").serialize(jodder); assertAttribute("firstname", json); assertAttribute("number", json); assertAttribute("exchange", json); assertAttributeMissing("areaCode", json); } @Test public void testExcludeAll() { JsonSerializer serializer = new JsonSerializer(); String json = serializer.exclude("*").serialize(jodder); assertEquals("{}", json); } @Test public void testMixedWildcards() { JsonSerializer serializer = new JsonSerializer(); serializer.include("firstname", "lastname").exclude("*"); String json = serializer.serialize(jodder); assertAttribute("firstname", json); assertStringValue("Igor", json); assertAttribute("lastname", json); assertStringValue("Spasic", json); assertAttributeMissing("class", json); assertAttributeMissing("phones", json); assertAttributeMissing("birthdate", json); serializer = new JsonSerializer(); serializer.include("firstname", "lastname", "phones.areaCode", "phones.exchange", "phones.number").exclude("*"); json = serializer.serialize(jodder); assertAttribute("firstname", json); assertStringValue("Igor", json); assertAttribute("lastname", json); assertStringValue("Spasic", json); assertAttributeMissing("class", json); assertAttribute("phones", json); assertAttributeMissing("birthdate", json); } @Test public void testCopyOnWriteList() { CopyOnWriteArrayList<Person> people = new CopyOnWriteArrayList<>(); people.add(jodder); people.add(modesty); String json = new JsonSerializer().serialize(people); assertAttribute("firstname", json); assertStringValue("Igor", json); assertStringValue("Modesty", json); } @Test public void testAnnotations() { HashMap<String, TestClass3> map = new HashMap<>(); map.put("String1", new TestClass3()); TestClass2 testElement = new TestClass2(); testElement.setMapOfJustice(map); String json = new JsonSerializer().serialize(testElement); assertAttributeMissing("mapOfJustice", json); assertAttributeMissing("name", json); assertEquals(-1, json.indexOf("testName2")); json = new JsonSerializer().include("mapOfJustice").serialize(testElement); assertAttribute("mapOfJustice", json); // make sure the name property value is missing! assertAttributeMissing( "name", json ) // conflicts since mapOfJustice contains an object with name in it assertEquals(-1, json.indexOf("testName2")); } @Test public void testTransient() { TestClass2 testElement = new TestClass2(); String json = new JsonSerializer().serialize(testElement); assertAttributeMissing("description", json); json = new JsonSerializer().include("description").serialize(testElement); assertAttribute("description", json); } @Test public void testSettersWithoutGettersAreMissing() { Friend friend = new Friend("Nugget", "Donkey Rider", "Slim"); String json = new JsonSerializer().include("*").serialize(friend); assertAttribute("nicknames", json); assertAttributeMissing("nicknamesAsArray", json); } @Test public void testIncludesExcludes() throws FileNotFoundException { Surfer surfer = Surfer.createSurfer(); String json = new JsonSerializer().serialize(surfer); assertAttribute("name", json); assertStringValue("jodd", json); assertAttribute("id", json); assertAttribute("split", json); assertAttribute("skill", json); assertAttribute("pipe", json); assertAttributeMissing("phones", json); // exclude pipe json = new JsonSerializer().excludeTypes(InputStream.class).serialize(surfer); assertAttribute("name", json); assertStringValue("jodd", json); assertAttribute("id", json); assertAttribute("split", json); assertAttribute("skill", json); assertAttributeMissing("pipe", json); assertAttributeMissing("phones", json); // exclude pipe (alt) json = new JsonSerializer().excludeTypes("*Stream").serialize(surfer); assertAttribute("name", json); assertStringValue("jodd", json); assertAttribute("id", json); assertAttribute("split", json); assertAttribute("skill", json); assertAttributeMissing("pipe", json); assertAttributeMissing("phones", json); // exclude s*, include phones json = new JsonSerializer().exclude("split").include("phones").excludeTypes("*Stream").serialize(surfer); assertAttribute("name", json); assertStringValue("jodd", json); assertAttribute("id", json); assertAttributeMissing("split", json); assertAttribute("skill", json); assertAttributeMissing("pipe", json); assertAttribute("phones", json); assertAttribute("exchange", json); json = new JsonSerializer().exclude("split").include("phones").exclude("phones.exchange").serialize(surfer); assertAttribute("phones", json); assertAttributeMissing("exchange", json); } @Test public void testSuperclass() { Hill hill = new Hill(); hill.setHeight("qwe"); hill.setName("aaa"); String json = new JsonSerializer().serialize(hill); assertAttribute("height", json); assertAttributeMissing("name", json); Mountain mountain = new Mountain(); mountain.setName("bbb"); mountain.setHeight("123"); mountain.setWild(true); JoddJson.serializationSubclassAware = false; JoddJson.annotationManager.reset(); json = new JsonSerializer().serialize(mountain); assertAttribute("height", json); assertAttribute("name", json); assertAttribute("wild", json); JoddJson.serializationSubclassAware = true; JoddJson.annotationManager.reset(); json = new JsonSerializer().serialize(mountain); assertAttribute("height", json); assertAttributeMissing("name", json); assertAttributeMissing("wild", json); } public static class Lucy { String name = "Lucy"; String address = null; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } @Test public void testSerializeWithCustomBeanSerializer() { JsonSerializer jsonSerializer = new JsonSerializer(); Lucy lucy = new Lucy(); String json = jsonSerializer.serialize(lucy); assertAttribute("address", json); assertAttribute("name", json); jsonSerializer.withSerializer(Object.class, new ObjectJsonSerializer() { public void serializeValue(final JsonContext jsonContext, Object value) { jsonContext.writeOpenObject(); BeanSerializer beanVisitor = new BeanSerializer(jsonContext, value) { @Override protected void onSerializableProperty(String propertyName, Class propertyType, Object value) { if (value == null) { return; } super.onSerializableProperty(propertyName, propertyType, value); } }; beanVisitor.serialize(); jsonContext.writeCloseObject(); } }); json = jsonSerializer.serialize(lucy); assertEquals("{\"name\":\"Lucy\"}", json); } // ---------------------------------------------------------------- custom asserts private int occurs(String str, String json) { int current = 0; int count = 0; while (current >= 0) { current = json.indexOf(str, current); if (current > 0) { count++; current += str.length(); } } return count; } private void assertAttributeMissing(String attribute, String json) { assertAttribute(attribute, json, false); } private void assertAttribute(String attribute, String json) { assertAttribute(attribute, json, true); } private void assertAttribute(String attribute, String json, boolean isPresent) { if (isPresent) { assertTrue(json.contains("\"" + attribute + "\":")); } else { assertFalse(json.contains("\"" + attribute + "\":")); } } private void assertStringValue(String value, String json, boolean isPresent) { if (isPresent) { assertTrue(json.contains("\"" + value + "\"")); } else { assertFalse(json.contains("\"" + value + "\"")); } } private void assertNumber(Number number, String json) { assertTrue(json.contains(number.toString())); } private void assertStringValueMissing(String value, String json) { assertStringValue(value, json, false); } private void assertStringValue(String value, String json) { assertStringValue(value, json, true); } private void assertSerializedTo(String original, String expected) { JsonSerializer serializer = new JsonSerializer(); String json = serializer.serialize(original); assertEquals(expected, json); } }