當前位置:成語大全網 - 書法字典 - 如何編寫JSON解析器

如何編寫JSON解析器

編寫JSON解析器實際上是壹個函數。它的輸入是壹個表示JSON的字符串,它的輸出是結構化的,並與語言本身的數據結構相對應。

與XML相比,JSON本身的結構非常簡單,只有幾種數據類型。以Java為例,對應的數據結構為:

“字符串”:字符串“:Java

數字:長型或雙型;Java的;

真/假:布爾型;Java的;

Null:空:Java

【數組】:Java列表

{“key“:“value“}:Java的映射

解析JSON類似於解析XML,最終它被解析成內存中的壹個對象。為了提高效率,使用stream幾乎是唯壹的選擇,即解析器只從頭掃描JSON字符串,並完全解析相應的數據結構。

從本質上講,解析器是壹個狀態機,只要能夠按照JSON定義的格式正確實現狀態轉換即可(參考http://www.json.org)。但是為了簡化代碼,我們不需要完全實現從角色到角色的狀態轉換。

解析器的輸入應該是壹個字符流,所以第壹步是獲得壹個讀取器,以便可以連續讀入下壹個字符。

在解析過程中,我們經常根據下壹個字符來決定狀態跳轉。這時候就涉及到回退的問題了,就是有時候我們不能用next()拿下壹個字符,而是用peek()拿下壹個字符,但是字符流的指針不動。因此,閱讀器接口不能滿足這壹要求,應進壹步封裝CharReader,可實現:

Char next():讀取下壹個字符並移動讀取器指針;

Char peek():讀取下壹個字符而不移動讀取器指針;

string next(int size):讀取指定的n個字符並移動指針;

Boolean hasMore():確定流是否結束。

JSON解析比其他文本解析簡單,因為任何JSON數據類型都只能通過下壹個字符來確定。仔細總結後可以發現,如果peek()返回的字符是某個字符,則可以預期讀取的數據類型為:

{:需要JSON對象;;

*需要壹個值;JSON對象的;

,:期望JSON對象的下壹組鍵值,或JSON數組的下壹個元素;

【:應為JSON數組;;

t:期待壹個真實;;

f:期待壹個假的;;

n:應為空值;;

“:應為字符串;;

0~9:需要壹個數字。

然而,對於單個字符來說,有太多的狀態需要匹配,因此有必要進壹步將字符流更改為令牌,這可以總結如下:

end _ document:JSON文檔的結尾;

BEGIN_OBJECT:啟動JSON對象;;

END_OBJECT:結束JSON對象;;

BEGIN_ARRAY:啟動JSON數組;;

END_ARRAY:結束JSON數組;;

SEP_COLON:讀壹個冒號;

SEP_COMMA:讀壹個逗號;

字符串:字符串;

布爾型:真或假;;

數字:壹個數字;

空值:空值。

然後,CharReader被進壹步封裝為TokenReader,它提供了以下接口:

Token readNextToken():讀取下壹個令牌;

Boolean readBoolean():讀取壹個布爾值;

Number readNumber():讀取壹個數字;

String readString():讀取壹個字符串;

Void readNull():讀取空值。

因為JSON對象和數組可以嵌套,所以在讀取時需要使用堆棧來存儲對象和數組。每當我們讀取壹個BEGIN_OBJECT時,我們都會創建壹個Map並對其進行堆棧;每當讀取BEGIN_ARRAY時,都會創建壹個列表並將其堆棧;每當讀取END_OBJECT和END_ARRAY時,就會彈出棧頂元素,根據新的棧頂元素判斷是否推棧。此外,對象的鍵也必須被推送到堆棧中,在讀取後面的值後,key-Value被推送到堆棧頂部的映射中。

如果讀取END_DOCUMENT時堆棧中只剩下壹個元素,則讀取是正確的,元素被返回,讀取結束。如果堆棧中還有多個元素,則JSON文檔格式不正確。

最後,JsonReader的核心解析代碼parse()負責不斷從TokenReader讀取令牌,根據當前狀態進行操作,然後設置下壹個令牌的預期狀態。如果它與預期狀態不匹配,則JSON格式無效。初始狀態設置為status _ expect _ single _ value | status _ expect _ begin _ object | status _ expect _ begin _ array,即預期讀取單個值{或【。循環的退出點是讀取END_DOCUMENT時。