本文主要分析部分JSONTokener类源码,JSONTokener用于处理JSON 文本,先看下面这个示例:
public class JSONObjectTest {
public static void main(String[] a){
//china两边没有引号,前面有一个空格
String json = "{\"msg\":\n\"i will be back.\",\r china:\"中国\"}";
JSONObject jsonObject = new JSONObject(json);
System.out.println(jsonObject.toString());
}
}
编译运行得到结果:
{"msg":"i will be back.","china":"中国"}
可以看出JSONTokener能处理字符串json中含有一般字符、转义字符和汉字等,代码中是按字符流来读取,汉字也是一个字符。先看看JSONTokener的构造器成员变量:
/** 当前字符在行里的位置,从1开始*/
private long character;
/** 标记是否读到文本的末尾 */
private boolean eof;
/**当前读到的字符的位置,从0开始*/
private long index;
/** 当前所在多少行,从0开始*/
private long line;
/** 当前字符的前一个字符,从0开始*/
private char previous;
/** Reader */
private final Reader reader;
/** 标记是否往后读*/
private boolean usePrevious;
/** 上一行读到的字符数量 */
private long characterPreviousLine;
public JSONTokener(String s) {
this(new StringReader(s));
}
public JSONTokener(InputStream inputStream) {
this(new InputStreamReader(inputStream));
}
public JSONTokener(Reader reader) {
this.reader = reader.markSupported()
? reader : new BufferedReader(reader);
this.eof = false;
this.usePrevious = false;
this.previous = 0;
this.index = 0;
this.character = 1;
this.characterPreviousLine = 0;
this.line = 1;
}
markSupported()是Reader类中的方法,查看是否支持mark 和 reset 方法。对JSON文本的操作体现到字符流读取上。下面一步步分析,先看JSONObject(JSONTokener x)源码:
public JSONObject(JSONTokener x) throws JSONException {
this();
char c;
String key;
//第一个字符一定是'{'
if (x.nextClean() != '{') {
throw x.syntaxError("A JSONObject text must begin with '{'");
}
for (;;) {
//读下一个
c = x.nextClean();
switch (c) {
case 0:
throw x.syntaxError("A JSONObject text must end with '}'");
case '}':
return;
default:
//回退一个字符
x.back();
//获取键
key = x.nextValue().toString();
}
// The key is followed by ':'.
c = x.nextClean();
if (c != ':') {
throw x.syntaxError("Expected a ':' after a key");
}
// Use syntaxError(..) to include error location
if (key != null) {
// Check if key exists
if (this.opt(key) != null) {
// key already exists
throw x.syntaxError("Duplicate key \"" + key + "\"");
}
// 获取value
Object value = x.nextValue();
if (value!=null) {
this.put(key, value);
}
}
// 可以';'或者','分隔,最后一个是
switch (x.nextClean()) {
case ';':
case ',':
if (x.nextClean() == '}') {
return;
}
//下一个字符不等于'}'时,便于获取下一个key
x.back();
break;
case '}':
return;
default:
throw x.syntaxError("Expected a ',' or '}'");
}
}
}
JSONTokener的方法nextClean()和next()是这样写的:
public char nextClean() throws JSONException {
for (;;) {
char c = this.next();
//字符c == 0表示已到文本的末尾,c > ' '表示只要ascii大于32的字符
if (c == 0 || c > ' ') {
return c;
}
}
}
public char next() throws JSONException {
int c;
//usePrevious为true时,倒退了一个字符
if (this.usePrevious) {
this.usePrevious = false;
c = this.previous;
} else {
try {
//读一个字符
c = this.reader.read();
} catch (IOException exception) {
throw new JSONException(exception);
}
}
if (c <= 0) { // End of stream
this.eof = true;
return 0;
}
this.incrementIndexes(c);
this.previous = (char) c;
return this.previous;
}
private void incrementIndexes(int c) {
if(c > 0) {
this.index++;
//换行
if(c=='\r') {
this.line++;
//前一行读到的字符数
this.characterPreviousLine = this.character;
this.character=0;
}else if (c=='\n') {
//previous等于'\r'时,'\n'算在前一行;不等时进行换行操作
if(this.previous != '\r') {
this.line++;
this.characterPreviousLine = this.character;
}
this.character=0;
} else {
this.character++;
}
}
}
上面几个方法标记当前读取的状态,和返回当前字符,通过该字符,判断获取key、value和嵌套JSON对象和数组。下面看key和value的获取:
public void back() throws JSONException {
//初始不能倒退,只能倒退一次
if (this.usePrevious || this.index <= 0) {
throw new JSONException("Stepping back two steps is not supported");
}
this.decrementIndexes();
this.usePrevious = true;
this.eof = false;
}
//incrementIndexes的相反操作
private void decrementIndexes() {
this.index--;
if(this.previous=='\r' || this.previous == '\n') {
this.line--;
this.character=this.characterPreviousLine ;
} else if(this.character > 0){
this.character--;
}
}
//获取key或者value
public Object nextValue() throws JSONException {
char c = this.nextClean();
String string;
switch (c) {
case '"':
case '\'':
return this.nextString(c); //两端有引号时,可以是单引号,但必须成对出现
case '{':
//JSON对象
this.back();
return new JSONObject(this);
case '[':
//JSON数组
this.back();
return new JSONArray(this);
}
//没有引号时,例子中china的两边没有引号
//放字符串值
StringBuilder sb = new StringBuilder();
//对该字符做了些限制,和key或value结束
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
sb.append(c);
c = this.next();
}
this.back();
string = sb.toString().trim();
if ("".equals(string)) {
throw this.syntaxError("Missing value");
}
//字符串转相应的基本类型
return JSONObject.stringToValue(string);
}
public String nextString(char quote) throws JSONException {
char c;
StringBuilder sb = new StringBuilder();
for (;;) {
c = this.next();
switch (c) {
case 0:
case '\n':
case '\r':
throw this.syntaxError("Unterminated string");
case '\\':
//key、value中可以有以下字符
c = this.next();
switch (c) {
case 'b':
sb.append('\b');
break;
case 't':
sb.append('\t');
break;
case 'n':
sb.append('\n');
break;
case 'f':
sb.append('\f');
break;
case 'r':
sb.append('\r');
break;
case 'u':
try {
sb.append((char)Integer.parseInt(this.next(4), 16));
} catch (NumberFormatException e) {
throw this.syntaxError("Illegal escape.", e);
}
break;
case '"':
case '\'':
case '\\':
case '/':
sb.append(c);
break;
default:
throw this.syntaxError("Illegal escape.");
}
break;
default:
if (c == quote) {
return sb.toString();
}
sb.append(c);
}
}
}
JSON文本的解析就是对字符流的操作,判定key和value的出现,以及一些特殊字符的处理和识别其意义,对一些不合法的JSON文本指出其不合法的位置和字符。