// 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)));
}
}