// *************************************************************************************************************************** | |
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * | |
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * | |
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * | |
// * with the License. You may obtain a copy of the License at * | |
// * * | |
// * http://www.apache.org/licenses/LICENSE-2.0 * | |
// * * | |
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * | |
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * | |
// * specific language governing permissions and limitations under the License. * | |
// *************************************************************************************************************************** | |
package org.apache.juneau.json; | |
import static org.apache.juneau.testutils.TestUtils.*; | |
import static org.junit.Assert.*; | |
import java.io.*; | |
import org.apache.juneau.*; | |
import org.apache.juneau.parser.*; | |
import org.apache.juneau.serializer.*; | |
import org.junit.*; | |
@SuppressWarnings({}) | |
public class JsonParserTest { | |
private static final JsonParser p = JsonParser.DEFAULT; | |
private static final JsonParser sp = JsonParser.DEFAULT_STRICT; | |
//==================================================================================================== | |
// Test invalid input | |
//==================================================================================================== | |
@Test | |
public void testInvalidJson() { | |
try { | |
p.parse("{\na:1,\nb:xxx\n}", Object.class); | |
fail("Exception expected."); | |
} catch (ParseException e) {} | |
} | |
@Test | |
public void testNonExistentAttribute() throws Exception { | |
String json = "{foo:,bar:}"; | |
ObjectMap m = p.parse(json, ObjectMap.class); | |
assertEquals("{foo:null,bar:null}", m.toString()); | |
} | |
@Test | |
public void testNonStringAsString() throws Exception { | |
String json = "123"; | |
String s; | |
// Strict mode does not allow unquoted values. | |
try { | |
sp.parse(json, String.class); | |
fail("Exception expected"); | |
} catch (Exception e) { | |
assertTrue(e.getMessage().contains("Did not find quote character")); | |
} | |
s = p.parse(json, String.class); | |
assertEquals("123", s); | |
json = " 123 "; | |
// Strict mode does not allow unquoted values. | |
try { | |
sp.parse(json, String.class); | |
fail("Exception expected"); | |
} catch (Exception e) { | |
assertTrue(e.getMessage().contains("Did not find quote character")); | |
} | |
s = p.parse(json, String.class); | |
assertEquals("123", s); | |
json = "{\"fa\":123}"; | |
try { | |
sp.parse(json, A.class); | |
fail("Exception expected"); | |
} catch (Exception e) { | |
assertTrue(e.getMessage().contains("Did not find quote character")); | |
} | |
A a = p.parse(json, A.class); | |
assertEquals("123", a.fa); | |
json = " { \"fa\" : 123 } "; | |
try { | |
sp.parse(json, A.class); | |
fail("Exception expected"); | |
} catch (Exception e) { | |
assertTrue(e.getMessage().contains("Did not find quote character")); | |
} | |
a = p.parse(json, A.class); | |
assertEquals("123", a.fa); | |
json = "'123'"; | |
try { | |
sp.parse(json, String.class); | |
fail("Exception expected"); | |
} catch (Exception e) { | |
assertTrue(e.getMessage().contains("Invalid quote character")); | |
} | |
} | |
public static class A { | |
public String fa; | |
} | |
@Test | |
public void testStrictMode() throws Exception { | |
JsonParser p = sp; | |
// Missing attribute values. | |
String json = "{\"foo\":,\"bar\":}"; | |
try { | |
p.parse(json, ObjectMap.class); | |
fail("Exception expected"); | |
} catch (ParseException e) { | |
assertTrue(e.getRootCause().getMessage().contains("Missing value detected.")); | |
} | |
// Single quoted values. | |
json = "{\"foo\":'bar'}"; | |
try { | |
p.parse(json, ObjectMap.class); | |
fail("Exception expected"); | |
} catch (ParseException e) { | |
assertTrue(e.getRootCause().getMessage().contains("Invalid quote character")); | |
} | |
// Single quoted attribute name. | |
json = "{'foo':\"bar\"}"; | |
try { | |
p.parse(json, ObjectMap.class); | |
fail("Exception expected"); | |
} catch (ParseException e) { | |
assertTrue(e.getRootCause().getMessage().contains("Invalid quote character")); | |
} | |
// Unquoted attribute name. | |
json = "{foo:\"bar\"}"; | |
try { | |
p.parse(json, ObjectMap.class); | |
fail("Exception expected"); | |
} catch (ParseException e) { | |
assertTrue(e.getRootCause().getMessage().contains("Unquoted attribute detected.")); | |
} | |
// Concatenated string | |
json = "{\"foo\":\"bar\"+\"baz\"}"; | |
try { | |
p.parse(json, ObjectMap.class); | |
fail("Exception expected"); | |
} catch (ParseException e) { | |
assertTrue(e.getRootCause().getMessage().contains("String concatenation detected.")); | |
} | |
// Concatenated string 2 | |
json = "{\"foo\":\"bar\" + \"baz\"}"; | |
try { | |
p.parse(json, ObjectMap.class); | |
fail("Exception expected"); | |
} catch (ParseException e) { | |
assertTrue(e.getRootCause().getMessage().contains("String concatenation detected.")); | |
} | |
json = "{\"foo\":/*comment*/\"bar\"}"; | |
try { | |
p.parse(json, ObjectMap.class); | |
fail("Exception expected"); | |
} catch (ParseException e) { | |
assertTrue(e.getRootCause().getMessage().contains("Javascript comment detected.")); | |
} | |
} | |
/** | |
* JSON numbers and booleans should be representable as strings and converted accordingly. | |
*/ | |
@Test | |
public void testPrimitivesAsStrings() throws Exception { | |
String json; | |
ReaderParser p = JsonParser.DEFAULT; | |
WriterSerializer s = SimpleJsonSerializer.DEFAULT; | |
json = "{f01:'1',f02:'1',f03:'true',f04:'true',f05:'1',f06:'1',f07:'1',f08:'1',f09:'1',f10:'1'}"; | |
B b = p.parse(json, B.class); | |
assertEquals("{f01:1,f02:1,f03:true,f04:true,f05:1.0,f06:1.0,f07:1,f08:1,f09:1,f10:1}", s.toString(b)); | |
json = "{f01:'',f02:'',f03:'',f04:'',f05:'',f06:'',f07:'',f08:'',f09:'',f10:''}"; | |
b = p.parse(json, B.class); | |
assertEquals("{f01:0,f02:0,f03:false,f04:false,f05:0.0,f06:0.0,f07:0,f08:0,f09:0,f10:0}", s.toString(b)); | |
} | |
public static class B { | |
public int f01; | |
public Integer f02; | |
public boolean f03; | |
public Boolean f04; | |
public float f05; | |
public Float f06; | |
public long f07; | |
public Long f08; | |
public byte f09; | |
public Byte f10; | |
} | |
//==================================================================================================== | |
// testInvalidJsonNumbers | |
// Lax parser allows octal and hexadecimal numbers. Strict parser does not. | |
//==================================================================================================== | |
@Test | |
public void testInvalidJsonNumbers() throws Exception { | |
JsonParser p1 = JsonParser.DEFAULT; | |
JsonParser p2 = JsonParser.DEFAULT_STRICT; | |
Number r; | |
// Lax allows blank strings interpreted as 0, strict does not. | |
String s = "\"\""; | |
r = p1.parse(s, Number.class); | |
assertEquals(0, r.intValue()); | |
assertTrue(r instanceof Integer); | |
try { | |
r = p2.parse(s, Number.class); | |
fail("Exception expected"); | |
} catch (ParseException e) { | |
assertTrue(e.getMessage().contains("Invalid JSON number")); | |
} | |
// Either should allow 0 or -0. | |
s = "0"; | |
r = p1.parse(s, Number.class); | |
assertEquals(0, r.intValue()); | |
assertTrue(r instanceof Integer); | |
r = p2.parse(s, Number.class); | |
assertEquals(0, r.intValue()); | |
assertTrue(r instanceof Integer); | |
s = "-0"; | |
r = p1.parse(s, Number.class); | |
assertEquals(0, r.intValue()); | |
assertTrue(r instanceof Integer); | |
r = p2.parse(s, Number.class); | |
assertEquals(0, r.intValue()); | |
assertTrue(r instanceof Integer); | |
// Lax allows 0123 and -0123, strict does not. | |
s = "0123"; | |
r = p1.parse(s, Number.class); | |
assertEquals(0123, r.intValue()); | |
assertTrue(r instanceof Integer); | |
try { | |
r = p2.parse(s, Number.class); | |
fail("Exception expected"); | |
} catch (ParseException e) { | |
assertTrue(e.getMessage().contains("Invalid JSON number")); | |
} | |
s = "-0123"; | |
r = p1.parse(s, Number.class); | |
assertEquals(-0123, r.intValue()); | |
assertTrue(r instanceof Integer); | |
try { | |
r = p2.parse(s, Number.class); | |
fail("Exception expected"); | |
} catch (ParseException e) { | |
assertTrue(e.getMessage().contains("Invalid JSON number")); | |
} | |
// Lax allows 0x123 and -0x123, strict does not. | |
s = "0x123"; | |
r = p1.parse(s, Number.class); | |
assertEquals(0x123, r.intValue()); | |
assertTrue(r instanceof Integer); | |
try { | |
r = p2.parse(s, Number.class); | |
fail("Exception expected"); | |
} catch (ParseException e) { | |
assertTrue(e.getMessage().contains("Invalid JSON number")); | |
} | |
s = "-0x123"; | |
r = p1.parse(s, Number.class); | |
assertEquals(-0x123, r.intValue()); | |
assertTrue(r instanceof Integer); | |
try { | |
r = p2.parse(s, Number.class); | |
fail("Exception expected"); | |
} catch (ParseException e) { | |
assertTrue(e.getMessage().contains("Invalid JSON number")); | |
} | |
} | |
//==================================================================================================== | |
// testUnquotedStrings | |
// Lax parser allows unquoted strings if POJO can be converted from a string. | |
//==================================================================================================== | |
@Test | |
public void testUnquotedStrings() throws Exception { | |
JsonParser p1 = JsonParser.DEFAULT; | |
JsonParser p2 = JsonParser.DEFAULT_STRICT; | |
String s = "foobar"; | |
C c = p1.parse(s, C.class); | |
assertEquals("f=foobar", c.toString()); | |
try { | |
p2.parse(s, C.class); | |
fail("Exception expected"); | |
} catch (ParseException e) { | |
// OK | |
} | |
} | |
public static class C { | |
String f; | |
public static C valueOf(String s) { | |
C c = new C(); | |
c.f = s; | |
return c; | |
} | |
@Override /* Object */ | |
public String toString() { | |
return "f="+f; | |
} | |
} | |
//==================================================================================================== | |
// testStreamsAutoClose | |
// Validates PARSER_autoCloseStreams. | |
//==================================================================================================== | |
@Test | |
public void testStreamsAutoClose() throws Exception { | |
ReaderParser p = JsonParser.DEFAULT.builder().autoCloseStreams().build(); | |
Object x; | |
Reader r; | |
r = reader("{foo:'bar'}{baz:'qux'}"); | |
x = p.parse(r, ObjectMap.class); | |
assertObjectEquals("{foo:'bar'}", x); | |
try { | |
x = p.parse(r, ObjectMap.class); | |
fail("Exception expected"); | |
} catch (Exception e) { | |
assertTrue(e.getMessage().contains("Reader is closed")); | |
} | |
} | |
//==================================================================================================== | |
// testMultipleObjectsInStream | |
// Validates that readers are not closed so that we can read streams of POJOs. | |
//==================================================================================================== | |
@Test | |
public void testMultipleObjectsInStream() throws Exception { | |
ReaderParser p = JsonParser.create().unbuffered().build(); | |
Object x; | |
Reader r; | |
r = reader("{foo:'bar'}{baz:'qux'}"); | |
x = p.parse(r, ObjectMap.class); | |
assertObjectEquals("{foo:'bar'}", x); | |
x = p.parse(r, ObjectMap.class); | |
assertObjectEquals("{baz:'qux'}", x); | |
r = reader("[123][456]"); | |
x = p.parse(r, ObjectList.class); | |
assertObjectEquals("[123]", x); | |
x = p.parse(r, ObjectList.class); | |
assertObjectEquals("[456]", x); | |
} | |
private Reader reader(String in) { | |
return new CloseableStringReader(in); | |
} | |
} |