说说org.json.JSONObject功能和源码(二)

本文主要分析部分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文本指出其不合法的位置和字符。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值