Json字符串解析原理、超大json對象的解析


 

 

概述

附上完整的代碼:

https://files.cnblogs.com/files/xcr1234/json.rar

一個類實現json解析核心代碼(ObjectParser),其他的類都是工具類
(入口類是Json)

 



JSON:JavaScript 對象表示法(JavaScript Object Notation)。

JSON 是存儲和交換文本信息的語法。類似 XML。

JSON 比 XML 更小、更快,更易解析。

在JSON中,分為6種對象:

  • 數字(整數或浮點數)
  • 字符串(在雙引號中)
  • 邏輯值(true 或 false)
  • 數組(JsonArray)
  • 對象(JsonObject)
  • null

基本對象的實現

JsonObject其實就是一個HashMap,JsonArray其實就是一個ArrayList.

 

public class JsonObject extends HashMap<String,Object> {

}

public class JsonArray extends ArrayList<Object> {

}

 

JSON字符串的解析

以這個字符串為例:

{“success”:true,”id”:-10.5,”employees”:[{“firstName”:”Bill”,”lastName”:”Gates”},{“firstName”:”George”,”lastName”:”Bush”},{“firstName”:”Thomas”,”lastName”:”Carter”}]}

我們保證在只掃描一次整個的情況下,就將json結構解析成功。

 

傳統的解析策略通常是通過詞法分析,將json分為一個個的token,而這些token有着自己的類型和值;再通過語法分析構建一棵抽象語法樹,進一步處理。比如""是一種,true/false又是一種。

其實根本不需要這么復雜。依我看來,json的token只有五種:true/false/null(歸為一種,因為它們是固定值)、number、string、object、array。也不用特別在意start和end的Token區分,比如 { 符號和 } 符號。從一個 { 符號開始,到下一個它對應的 } 符號都是屬於同一個json object的。這里的 { 與 } 、[ 與 ] 符號都是一一對應的。
我設計了一個nextObject()方法,它可以解析出json字符串中的下一個對象,然后在適當的時候裝配即可。


nextObject方法的實現

提取字符

 

    public static boolean isSpace(char c){
        return c == ' ' || c == '\r' || c == '\n';
    }

    //方法得到當前字符,忽略空格、換行符
    private char getChar(){
        char c = json.charAt(pos);
        while(isSpace(c)){
            pos++;
            c = getCurrentChar();
        }
        return c;
    }

 

上面方法是消耗掉所有空白字符,直到讀取到一個非空白字符,isSpace方法用於判斷一個字符是否屬於空白字符,pos表示當前指針指向的那個字符。也就是說,DFA從起始狀態開始,若讀到一個空字符,會在起始狀態不斷循環,直到遇到非空字符,狀態轉移情況如下:

 

解析

 

根據提取到的字符,轉入不同的解析方法中,

例如字符是t,說明值可能是true,只需檢查后面三個字符,如果是r、u、e,則可以直接返回true。

字符是f,說明值可能是false,只需檢查后面四個字符,如果是a、l、s、e,則可以直接返回false。

碰到 \”,說明是字符串,在下一個\”出現之前,把掃描出來的字符都當成字符串中的字符,放到一個StringBuilder中去。

碰到 [ 符號,說明是數組了,就需要new一個JsonArray,在下一個 ] 符號出現之前,調用nextObject方法,把解析到的對象都放到這個JsonArray里面去。

碰到 { 符號,說明是JsonObject,就new一個JsonObject,這里每次需要連續調用兩次nextObject,第一次結果作為key,第二次結果作為value。放到JsonObject中去。

解析boolean、null值

這類值的字符串只有固定的三種true、false、null,是最好解析的。在掃描到第一個字符為t、f、n時,只需檢測后續字符是否符合固定值就可以了。checkChars方法實現了這個功能,chars是固定的序列,如果檢測通過則返回true,否則返回false。

private boolean checkChars(char ...chars){
        for(char ch : chars){
            char c = getCurrentCharNext();   //得到當前字符,包括空格、換行符。將指針指向下一個字符
            if(Character.toLowerCase(ch) != Character.toLowerCase(c)){
                return false;
            }
        }
        return true;
    }

 

如果是true,就是`checkChars('t','r','u','e')`

解析數字

解析數字的實現是parseNumber方法,我們先new一個StringBuilder,向后掃描只要碰到0-9或者+-小數點,就添加到這個StringBuilder當中去,否則就StringBuilder.toString,將這個字符串轉換成數字。

如果包含小數點,就用double,否則就用integer。

 

解析字符串

在json中字符串都是以雙引號”開頭,再以雙引號”結尾的。當掃描到雙引號”時,new一個StringBuilder,然后在下一個雙引號”出現之前的每一個字符都需要添加到這個StringBuilder中去。需要注意的一點,字符串中是可能出現轉義字符的。因此在掃描到一個字符為斜杠\時,需要取出下一個字符進行特殊處理。

 

解析JsonObject

連續調用兩次nextObject,第一次結果作為key,第二次結果作為value。放到JsonObject中去。
注意逗號和冒號的處理。

JsonArray的解析

 在下一個 ] 符號出現之前,遞歸調用nextObject方法,把解析到的對象都放到這個JsonArray里面去。

 

返回

由於nextObject只返回一個對象,我們用nextObject方法處理整個json字符串。那么nextObject方法就會得到你需要的JsonObject。

 

超大json對象的解析

參考 http://blog.csdn.net/dxiaolai/article/details/76359332

在大數據量的json場景下,不必將整個json字符串全部解析成json object后再處理,而是通過迭代器模式我們可以在解析字符串的同時使用對象。這樣可以大大的提高程序的執行效率。

擴展ObjectParser類,使其成為一個迭代器,

 

public class ObjectParser implements Iterator<Object>{

    public Object next(){
        return nextObject();
    }

    public boolean hasNext(){
        return pos < json.length();
    }

    @Override
    public void remove() {

    }

}

這樣就可以邊解析邊使用對象了。

 

ObjectParser parser = new ObjectParser ("json");
while(parser.hasNext()){
    Object object = parser.next();
}

 

超大的json串,通常是以流的方式提供,我們不必要一次性將流字節全部讀入內存,而是可以逐字符的解析。每次讀取若干個字符,解析成對象;實現方式是使用BuffererReader,修改getChar等方法,每次讀字符時從BuffererReader中讀取。配合上面的迭代器模式,可考慮將一個BuffererReader封裝成Iterator<Object>。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM