// 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.Jodd; import jodd.io.FileUtil; import jodd.io.StreamUtil; import jodd.json.meta.JSON; import jodd.json.fixtures.model.FooBar; import jodd.json.fixtures.model.HitList; import jodd.util.RandomString; import jodd.util.StringUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.math.BigInteger; import java.net.URL; import java.util.List; import java.util.Map; import java.util.zip.GZIPInputStream; import static jodd.util.ArraysUtil.ints; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class JsonParserTest { protected String dataRoot; @Before public void setUp() throws Exception { if (dataRoot != null) { return; } URL data = JsonParserTest.class.getResource("data"); if (data != null) { dataRoot = data.getFile(); } } @After public void tearDown() { JoddJson.classMetadataName = null; } @Test public void testSimpleJson() { JsonParser jsonParser = new JsonParser(); Object value = jsonParser.parse("true"); assertNotNull(value); assertEquals(Boolean.TRUE, value); value = jsonParser.parse(" true "); assertEquals(Boolean.TRUE, value); value = jsonParser.parse("\t\tfalse\n"); assertEquals(Boolean.FALSE, value); value = jsonParser.parse("\t\"foo\"\n"); assertEquals("foo", value); value = jsonParser.parse("1"); assertTrue(value instanceof Integer); assertEquals(Integer.valueOf(1), value); value = jsonParser.parse("-1234"); assertTrue(value instanceof Integer); assertEquals(Integer.valueOf(-1234), value); value = jsonParser.parse("" + Integer.MAX_VALUE); assertTrue(value instanceof Integer); assertEquals(Integer.valueOf(Integer.MAX_VALUE), value); value = jsonParser.parse("" + Integer.MIN_VALUE); assertTrue(value instanceof Integer); assertEquals(Integer.valueOf(Integer.MIN_VALUE), value); long l = Integer.MAX_VALUE + 1l; value = jsonParser.parse("" + l); assertTrue(value instanceof Long); assertEquals(Long.valueOf(l), value); l = Integer.MIN_VALUE - 1l; value = jsonParser.parse("" + l); assertTrue(value instanceof Long); assertEquals(Long.valueOf(l), value); value = jsonParser.parse("1.2"); assertTrue(value instanceof Double); assertEquals(Double.valueOf(1.2), value); value = jsonParser.parse("-12.34"); assertTrue(value instanceof Double); assertEquals(Double.valueOf(-12.34), value); value = jsonParser.parse("1e3"); assertTrue(value instanceof Integer); assertEquals(Integer.valueOf(1000), value); l = Long.MAX_VALUE; value = jsonParser.parse("" + l); assertTrue(value instanceof Long); assertEquals(Long.valueOf(l), value); l = Long.MIN_VALUE; value = jsonParser.parse("" + l); assertTrue(value instanceof Long); assertEquals(Long.valueOf(l), value); BigInteger bi = BigInteger.valueOf(Long.MAX_VALUE); bi = bi.add(BigInteger.ONE); value = jsonParser.parse(bi.toString()); assertTrue(value instanceof BigInteger); assertEquals(bi, value); bi = bi.subtract(BigInteger.ONE); value = jsonParser.parse(bi.toString()); assertTrue(value instanceof Long); assertEquals(Long.MAX_VALUE, ((Long) value).longValue()); bi = BigInteger.valueOf(Long.MIN_VALUE); bi = bi.subtract(BigInteger.ONE); value = jsonParser.parse(bi.toString()); assertTrue(value instanceof BigInteger); assertEquals(bi, value); } @Test public void testSimpleConversions() { JsonParser jsonParser = new JsonParser(); assertEquals(173, jsonParser.parse("\"173\"", Integer.class).intValue()); assertEquals("123", jsonParser.parse("123", String.class)); assertEquals(1, jsonParser.parse("true", Integer.class).intValue()); assertEquals(0, jsonParser.parse("false", Integer.class).intValue()); } @Test public void testConversionsToObject() { JsonParser jsonParser = new JsonParser(); assertEquals("173", jsonParser.parse("\"173\"", Object.class)); assertEquals(123, jsonParser.parse("123", Object.class)); assertEquals(true, jsonParser.parse("true", Object.class)); assertTrue(jsonParser.parse("[]", Object.class) instanceof List); assertTrue(jsonParser.parse("{}", Object.class) instanceof Map); } @Test public void testStringEscapes() { JsonParser jsonParser = new JsonParser(); assertEquals("\n4", jsonParser.parse("\"\\n\\u0034\"")); try { jsonParser.parse("\"\\u034\""); fail(); } catch (Exception ignore) { } } @Test public void testFeatures() { JsonParser jsonParser = new JsonParser(); Map map = jsonParser.parse("{}"); assertTrue(map.isEmpty()); map = jsonParser.parse("{ \"v\":\"1\"}"); assertEquals(1, map.size()); assertEquals("1", map.get("v")); map = jsonParser.parse("{ \"v\":\"1\"\r\n}"); assertEquals(1, map.size()); assertEquals("1", map.get("v")); map = jsonParser.parse("{ \"v\":1}"); assertEquals(1, map.size()); assertEquals("1", map.get("v").toString()); map = jsonParser.parse("{ \"v\":\"ab'c\"}"); assertEquals(1, map.size()); assertEquals("ab'c", map.get("v").toString()); map = jsonParser.parse("{ \"PI\":3.141E-10}"); assertEquals(1, map.size()); assertEquals(3.141E-10, ((Double)map.get("PI")).doubleValue(), 0.001E-10); map = jsonParser.parse("{ \"PI\":3.141e-10}"); assertEquals(1, map.size()); assertEquals(3.141e-10, ((Double)map.get("PI")).doubleValue(), 0.001E-10); map = jsonParser.parse("{ \"v\":12345123456789}"); assertEquals(1, map.size()); assertEquals(12345123456789L, ((Long)map.get("v")).longValue()); map = jsonParser.parse("{ \"v\":123456789123456789123456789}"); assertEquals(1, map.size()); assertEquals("123456789123456789123456789", map.get("v").toString()); List list = jsonParser.parse("[ 1,2,3,4]"); assertEquals(4, list.size()); assertEquals(1, ((Integer)list.get(0)).intValue()); assertEquals(2, ((Integer)list.get(1)).intValue()); assertEquals(3, ((Integer)list.get(2)).intValue()); assertEquals(4, ((Integer)list.get(3)).intValue()); list = jsonParser.parse("[ \"1\",\"2\",\"3\",\"4\"]"); assertEquals(4, list.size()); assertEquals("1", list.get(0).toString()); assertEquals("2", list.get(1).toString()); assertEquals("3", list.get(2).toString()); assertEquals("4", list.get(3).toString()); list = jsonParser.parse("[ { }, { },[]]"); assertEquals(3, list.size()); assertEquals(0, ((Map)list.get(0)).size()); assertEquals(0, ((Map)list.get(1)).size()); assertEquals(0, ((List)list.get(2)).size()); map = jsonParser.parse("{ \"v\":\"\\u2000\\u20ff\"}"); assertEquals(1, map.size()); assertEquals("\u2000\u20ff", map.get("v")); map = jsonParser.parse("{ \"v\":\"\\u2000\\u20FF\"}"); assertEquals(1, map.size()); assertEquals("\u2000\u20FF", map.get("v")); map = jsonParser.parse("{ \"a\":\"hp://foo\"}"); assertEquals(1, map.size()); assertEquals("hp://foo", map.get("a")); map = jsonParser.parse("{ \"a\":null}"); assertEquals(1, map.size()); assertNull(map.get("a")); map = jsonParser.parse("{ \"a\" : true }"); assertEquals(1, map.size()); assertEquals(Boolean.TRUE, map.get("a")); map = jsonParser.parse("{ \"v\":1.7976931348623157E308}"); assertEquals(1, map.size()); assertEquals(1.7976931348623157E308, ((Double)map.get("v")).doubleValue(), 1e300); } @Test public void testSimpleMap() { JsonParser jsonParser = new JsonParser(); Object value = jsonParser.parse(" { \"one\" : true} "); assertNotNull(value); assertTrue(value instanceof Map); Map map = (Map) value; assertEquals(1, map.size()); assertEquals(Boolean.TRUE, map.get("one")); value = jsonParser.parse("{ \"one\" : { \"two\" : false }, \"three\" : true}"); assertNotNull(value); assertTrue(value instanceof Map); map = (Map) value; assertEquals(2, map.size()); assertEquals(Boolean.TRUE, map.get("three")); map = (Map) map.get("one"); assertEquals(1, map.size()); assertEquals(Boolean.FALSE, map.get("two")); } @Test public void testSimpleArray() { JsonParser jsonParser = new JsonParser(); Object value = jsonParser.parse("[true, false, true, [\"A\", \"BB\"]]"); assertNotNull(value); List list = (List) value; assertEquals(4, list.size()); assertEquals(Boolean.TRUE, list.get(0)); assertEquals(Boolean.FALSE, list.get(1)); assertEquals(Boolean.TRUE, list.get(2)); list = (List) list.get(3); assertEquals(2, list.size()); assertEquals("A", list.get(0)); assertEquals("BB", list.get(1)); } static class ArrHolder { int[] pos; } @Test public void testSimpleMatrix() { JsonParser jsonParser = new JsonParser(); ArrHolder arrHolder = jsonParser.parse("{\"pos\":[1,2,3,4,5,6,7,8]}", ArrHolder.class); assertArrayEquals(ints(1,2,3,4,5,6,7,8), arrHolder.pos); JsonParser jsonParser2 = new JsonParser(); int[] ints = jsonParser2.parse("[1,2,3,4]", int[].class); assertEquals(4, ints.length); assertEquals(1, ints[0]); assertEquals(4, ints[3]); JsonParser jsonParser3 = new JsonParser(); int[][] matrix = jsonParser3.parse("[[1,2,3],[7,8,9]]", int[][].class); assertEquals(2, matrix.length); assertArrayEquals(ints(1,2,3), matrix[0]); assertArrayEquals(ints(7,8,9), matrix[1]); } // ---------------------------------------------------------------- object tree public static class Bar { Integer amount; public Integer getAmount() { return amount; } public void setAmount(Integer amount) { this.amount = amount; } } public static interface Inter { public char getSign(); } public static class InterImpl implements Inter { protected char sign; public char getSign() { return sign; } public void setSign(char sign) { this.sign = sign; } } public static class Foo { String name; long id; Bar bar; Inter inter; public long getId() { return id; } public void setId(long id) { this.id = id; } public Bar getBar() { return bar; } public void setBar(Bar bar) { this.bar = bar; } public Inter getInter() { return inter; } public void setInter(Inter inter) { this.inter = inter; } } public static class Wildcard { Object value; public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } } @Test public void testSimpleObject() { JsonParser jsonParser = new JsonParser(); Foo foo = jsonParser .map("inter", InterImpl.class) .parse( "{" + "\"aaa\": 123," + "\"name\": \"jodd\"," + "\"id\": \"173\"," + "\"bar\": {" + "\"amount\" : \"-23\"" + "}," + "\"inter\": {" + "\"sign\" : \"W\"" + "}" + "}", Foo.class); assertNotNull(foo); assertEquals("jodd", foo.name); assertEquals(173, foo.getId()); assertNotNull(foo.bar); assertEquals(-23, foo.bar.getAmount().intValue()); } @Test public void testObjectAttribute() { JsonParser jsonParser = new JsonParser(); jsonParser.map(Wildcard.class); Wildcard wildcard = jsonParser.parse("{\"value\":1}"); assertNotNull(wildcard); assertEquals(1, wildcard.getValue()); wildcard = jsonParser.parse("{\"value\":\"str\"}"); assertNotNull(wildcard); assertEquals("str", wildcard.getValue()); wildcard = jsonParser.parse("{\"value\":[1,2,3]}"); assertNotNull(wildcard); assertTrue(wildcard.getValue() instanceof List); assertArrayEquals(new Object[] {1, 2, 3}, ((List) wildcard.getValue()).toArray()); wildcard = jsonParser.parse( "{" + "\"value\": {" + "\"key\": \"value\"," + "\"inner\": {" + "\"key\": \"value\"" + "}" + "}" + "}"); assertNotNull(wildcard); assertTrue(wildcard.getValue() instanceof Map); Map mapValue = (Map) wildcard.getValue(); assertEquals(2, mapValue.size()); assertEquals("value", mapValue.get("key")); assertTrue(mapValue.get("inner") instanceof Map); Map innerValue = (Map) mapValue.get("inner"); assertEquals(1, innerValue.size()); assertEquals("value", innerValue.get("key")); } // ---------------------------------------------------------------- complex public static class Aaa { private Bar bar; private List<Byte> numbers; private List<Bar> bars; private List<Inter> inters; private int[] years; private Bar[] bongos; public Bar getBar() { return bar; } public void setBar(Bar bar) { this.bar = bar; } public List<Byte> getNumbers() { return numbers; } public void setNumbers(List<Byte> numbers) { this.numbers = numbers; } public List<Bar> getBars() { return bars; } public void setBars(List<Bar> bars) { this.bars = bars; } public List<Inter> getInters() { return inters; } public void setInters(List<Inter> inters) { this.inters = inters; } public int[] getYears() { return years; } public void setYears(int[] years) { this.years = years; } public Bar[] getBongos() { return bongos; } public void setBongos(Bar[] bongos) { this.bongos = bongos; } } @Test public void testComplexObject() throws IOException { JoddJson.classMetadataName = "class"; JsonParser jsonParser = new JsonParser(); String json = FileUtil.readString(new File(dataRoot, "complex.json")); Aaa aaa = jsonParser .map("numbers.values", Byte.class) .parse(json, Aaa.class); assertNotNull(aaa); assertEquals(84, aaa.getBar().getAmount().intValue()); List<Byte> numbers = aaa.getNumbers(); assertEquals(2, numbers.size()); assertEquals(19, numbers.get(0).byteValue()); assertEquals(21, numbers.get(1).byteValue()); List<Bar> bars = aaa.getBars(); assertEquals(2, bars.size()); assertEquals(5, bars.get(0).getAmount().intValue()); assertEquals(305, bars.get(1).getAmount().intValue()); List<Inter> inters = aaa.getInters(); assertEquals(3, inters.size()); assertEquals('a', inters.get(0).getSign()); assertEquals('b', inters.get(1).getSign()); assertEquals('c', inters.get(2).getSign()); int[] years = aaa.getYears(); assertEquals(3, years.length); assertEquals(1975, years[0]); assertEquals(2001, years[1]); assertEquals(2013, years[2]); Bar[] bongos = aaa.getBongos(); assertEquals(3, bongos.length); assertEquals(15, bongos[0].getAmount().intValue()); assertEquals(35, bongos[1].getAmount().intValue()); assertEquals(95, bongos[2].getAmount().intValue()); } // ---------------------------------------------------------------- complex maps public static class User { private String name; private Map<String, Bar> bars; private Map<String, Inter> inters; public String getName() { return name; } public void setName(String name) { this.name = name; } public Map<String, Bar> getBars() { return bars; } public void setBars(Map<String, Bar> bars) { this.bars = bars; } public Map<String, Inter> getInters() { return inters; } public void setInters(Map<String, Inter> inters) { this.inters = inters; } } @Test public void testComplexMaps() throws IOException { JsonParser jsonParser = new JsonParser(); String json = FileUtil.readString(new File(dataRoot, "complexMaps.json")); User user = jsonParser .map("inters.values", InterImpl.class) .parse(json, User.class); assertNotNull(user); assertEquals("Mak", user.getName()); Map<String, Bar> bars = user.getBars(); assertEquals(2, bars.size()); assertEquals(12300, bars.get("123").getAmount().intValue()); assertEquals(45600, bars.get("456").getAmount().intValue()); Map<String, Inter> inters = user.getInters(); assertEquals(3, inters.size()); } @Test public void testActionLabel() throws Exception { JsonParser jsonParser = new JsonParser(); String json = FileUtil.readString(new File(dataRoot, "actionLabel.json")); Map<String, Object> map; try { map = jsonParser.parse(json); } catch (Exception ex) { fail(ex.toString()); throw ex; } map = (Map<String, Object>) map.get("menu"); assertEquals("SVG Viewer", map.get("header")); List<String> items = (List<String>) map.get("items"); assertEquals(22, items.size()); assertNull(items.get(2)); assertNotNull(items.get(3)); } @Test public void testCitmCatalog() throws Exception { FileInputStream fis = new FileInputStream(new File(dataRoot, "citm_catalog.json.gz")); ByteArrayOutputStream out = new ByteArrayOutputStream(); StreamUtil.copy(new GZIPInputStream(fis), out); String json = out.toString("UTF-8"); fis.close(); JsonParser jsonParser = new JsonParser(); Map<String, Object> map; try { jsonParser.parse(json); map = jsonParser.parse(json); } catch (Exception ex) { fail(ex.toString()); throw ex; } assertNotNull(map); } @Test public void testString() { assertEquals("123", new JsonParser().parse("\"" + "123" + "\"")); assertEquals("12\n3", new JsonParser().parse("\"" + "12\\n3" + "\"")); String big = RandomString.getInstance().randomAlpha(510); String jbig = big + "\\n"; String rbig = big + "\n"; assertEquals(512, jbig.length()); assertEquals(rbig, new JsonParser().parse("\"" + jbig + "\"")); jbig += "x"; rbig += "x"; assertEquals(rbig, new JsonParser().parse("\"" + jbig + "\"")); jbig = "12" + jbig; rbig = "12" + rbig; assertEquals(rbig, new JsonParser().parse("\"" + jbig + "\"")); } @Test public void testJsonModule() { assertTrue(Jodd.isModuleLoaded(Jodd.JSON)); } @Test public void testInvalidJson() { try { new JsonParser().parse("\"" + "123" + "\","); fail(); } catch (JsonException ignore) { } try { new JsonParser().parse("{\"aa\":\"" + "123" + "\",}"); fail(); } catch (JsonException ignore) { } } @Test public void testKeys() { String json = "{\"123\" : \"name\"}"; Map<Long, String> map = new JsonParser().map("keys", Long.class).parse(json); assertEquals(1, map.size()); assertEquals("name", map.get(Long.valueOf(123))); json = "{\"eee\" : {\"123\" : \"name\"}}"; Map<String, Map<Long, String>> map2 = new JsonParser().map("values.keys", Long.class).parse(json); assertEquals(1, map2.size()); map = map2.get("eee"); assertEquals(1, map.size()); assertEquals("name", map.get(Long.valueOf(123))); } public static class MapHolder { Map<Long, Long[]> data; public Map<Long, Long[]> getData() { return data; } public void setData(Map<Long, Long[]> data) { this.data = data; } } @Test public void testMapOfListArrays() { String json = "{\"data\" : {\"123\" : [1,2,3]}}"; MapHolder mapHolder = new JsonParser().parse(json, MapHolder.class); Map<Long, Long[]> data = mapHolder.getData(); assertEquals(1, data.size()); Long[] longs = data.get(Long.valueOf(123)); assertNotNull(longs); } public static class Glista { @JSON(name = "first_name") private String firstName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } } @Test public void testAnnotationNameChangeFirstTime() { String json = "{\"first_name\":\"Djordje\"}"; Glista jsonGlista = new JsonParser().parse(json, Glista.class); assertEquals("Djordje", jsonGlista.getFirstName()); } @Test public void testEscapeAtTheEndOfLongString() { String s = StringUtil.repeat('A', 800); String json = "\"" + s + "\\n\""; try { new JsonParser().parse(json); } catch (Exception ex) { fail(ex.toString()); } } @Test public void testNamesWithDots() { String json = "{\"foo.bar\":123}"; FooBar fooBar = new JsonParser().parse(json, FooBar.class); assertEquals(123, fooBar.getValue().intValue()); } @Test public void testSets() { String json = "{\"names\":[\"Pig\",\"Joe\"],\"numbers\":[173,22]}"; HitList hitList = JsonParser.create().parse(json, HitList.class); assertNotNull(hitList); assertEquals(2, hitList.getNames().size()); assertTrue(hitList.getNames().contains("Pig")); assertTrue(hitList.getNames().contains("Joe")); assertEquals(2, hitList.getNumbers().size()); assertTrue(hitList.getNumbers().contains(Integer.valueOf(173))); assertTrue(hitList.getNumbers().contains(Integer.valueOf(22))); } }