今天遇到有同事在使用fastjson的JSONObject时,直接在parse方法中传入了一个非json格式的字符串,造成有时候报错,有时候又能正常返回。
问题现象
当你传入一个数值类型时,可以正常解析,当你传入一个非数值类型时,解析就报错了。
public class TestJson {
public static void main(String[] args) {
Object parse = JSONObject.parse("12345678");
System.out.println(parse + ", classType: " + parse.getClass());
Object parse1 = JSONObject.parse("A12345678");
System.out.println(parse1 + ", classType: " + parse1.getClass());
}
}
12345678, classType: class java.lang.Integer
Exception in thread "main" com.alibaba.fastjson.JSONException: syntax error, pos 1, json : A12345678
at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1436)
at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1322)
at com.alibaba.fastjson.JSON.parse(JSON.java:152)
at com.alibaba.fastjson.JSON.parse(JSON.java:162)
at com.alibaba.fastjson.JSON.parse(JSON.java:131)
at com.ykc.TestJson.main(TestJson.java:12)
Process finished with exit code 1
其实遇到这个问题时,令我奇怪的是这两个方法都应该报错,因为我一直认为这个方法应该只能解析Json格式的字符串才对。
PS:我完全不知道这位同事是怎么想的,为什么要用parse去解析一个非Json的格式的字符串。。。
简单翻了一下源码,主要就是这个方法,发现原来解析时还兼容了一些其他类型的参数,具体源码就不看了,也没什么特别难以理解的。
public final void nextToken() {
sp = 0;
for (;;) {
pos = bp;
if (ch == '/') {
skipComment();
continue;
}
if (ch == '"') {
scanString();
return;
}
if (ch == ',') {
next();
token = COMMA;
return;
}
//支持数值
if (ch >= '0' && ch <= '9') {
scanNumber();
return;
}
//这个是负号的意思。。。所以还支持负数
if (ch == '-') {
scanNumber();
return;
}
switch (ch) {
case '\'':
if (!isEnabled(Feature.AllowSingleQuotes)) {
throw new JSONException("Feature.AllowSingleQuotes is false");
}
scanStringSingleQuote();
return;
case ' ':
case '\t':
case '\b':
case '\f':
case '\n':
case '\r':
next();
break;
//还有一些true,false,null这样的
case 't': // true
scanTrue();
return;
case 'f': // false
scanFalse();
return;
case 'n': // new,null
scanNullOrNew();
return;
case 'T':
case 'N': // NULL
case 'S':
case 'u': // undefined
scanIdent();
return;
case '(':
next();
token = LPAREN;
return;
case ')':
next();
token = RPAREN;
return;
case '[':
next();
token = LBRACKET;
return;
case ']':
next();
token = RBRACKET;
return;
case '{':
next();
token = LBRACE;
return;
case '}':
next();
token = RBRACE;
return;
case ':':
next();
token = COLON;
return;
case ';':
next();
token = SEMI;
return;
case '.':
next();
token = DOT;
return;
case '+':
next();
scanNumber();
return;
case 'x':
scanHex();
return;
default:
if (isEOF()) { // JLS
if (token == EOF) {
throw new JSONException("EOF error");
}
token = EOF;
pos = bp = eofPos;
} else {
if (ch <= 31 || ch == 127) {
next();
break;
}
lexError("illegal.char", String.valueOf((int) ch));
next();
}
return;
}
}
}
看一下代码执行效果
public class TestJson {
public static void main(String[] args) {
Object parse1 = JSONObject.parse("true");
System.out.println(parse1 + ", classType: " + parse1.getClass());
Object parse2 = JSONObject.parse("1");
System.out.println(parse2 + ", classType: " + parse2.getClass());
Object parse3 = JSONObject.parse("-1");
System.out.println(parse3 + ", classType: " + parse3.getClass());
Object parse4 = JSONObject.parse("1.1");
System.out.println(parse4 + ", classType: " + parse4.getClass());
Object parse5 = JSONObject.parse("null");
System.out.println(parse5);
}
}
true, classType: class java.lang.Boolean
1, classType: class java.lang.Integer
-1, classType: class java.lang.Integer
1.1, classType: class java.math.BigDecimal
null
Process finished with exit code 0
感觉这个方法有点小坑啊,关键赤裸裸的方法居然一点说明也没有。。。
public static Object parse(String text) {
return parse(text, DEFAULT_PARSER_FEATURE);
}