承接上篇,本文深入剖析 Delphi 版 JSONTokener 的核心解析邏輯。此單元移植自 JSON.org 官方 Java 實作,並針對 Delphi 語言特性(無垃圾回收、無基本型別包裝類別)進行調整,引入智慧指標與自訂包裝類別。以下詳細說明關鍵方法實作。
TJSONTokener 封裝字串來源與當前索引,提供字元級、Token 級導航與解析功能。主要欄位:
fMySource: string - 待解析 JSON 字串fMyIndex: Integer - 當前讀取位置Next / Back:基本前進/後退一字元。NextClean:跳過空白字元、單行註釋(//)、多行註釋(/* */)與 Shell 風格註釋(#),回傳下一個有效字元。這使解析器能容忍帶註釋的非標準 JSON。處理雙引號或單引號包裹的字串,支援跳脫序列:\b \t \n \f \r \\ \" \' \uXXXX \xXX。遇到未閉合字串拋出 EJSONException。
NextTo(delimiters):讀取直到遇到分隔字元集、換行或結尾。NextValue:核心解析入口,根據首字元分派:
{ → 建立 TJSONObject(遞迴解析)[ 或 ( → 建立 TJSONArraytrue/false/null、數值(整數/長整數/雙精度/十六進制/八進制),失敗則視為字串。採用多層 try...except 級聯:Integer → Int64 → Double → String。特殊支援 0x 十六進制與 0 開頭八進制(非標準 JSON),提升相容性。
所有回傳物件皆包裝於 IAutoPtr<TObject> 介面,模擬 Java 參考計數語意,自動管理 TStringObject、TBooleanObject、TIntegerObject、TLongObject、TDoubleObject、TJSONObject、TJSONArray 生命週期,避免記憶體洩漏。
(節錄關鍵實作,完整碼見原文)
unit JSONTokener;
interface
uses SysUtils, StrUtils, AutoPtr, JSONException;
type
TJSONTokener = class
private
fMyIndex: Integer;
fMySource: string;
public
constructor Create(aMySource: string); virtual;
procedure Back;
class function DeHexChar(c: Char): Integer;
function More: Boolean;
function Next: Char; overload;
function Next(c: Char): Char; overload;
function Next(n: Integer): string; overload;
function SyntaxError(aMsg: string): EJSONException;
function ToString: string; override;
function NextClean: Char;
function NextString(quote: Char): string;
function NextTo(delimiters: string): string; overload;
function NextTo(d: Char): string; overload;
function NextValue: IAutoPtr<TObject>;
function SkipTo(toc: Char): Char;
function SkipPast(tos: string): Boolean;
end;
implementation
uses StringObject, BooleanObject, IntegerObject, LongObject, DoubleObject,
Utils, JSONObject, JSONArray;
{ 關鍵實作範例 }
function TJSONTokener.NextClean: Char;
var c: Char;
begin
while True do
begin
c := Next;
if c = '/' then
begin
case Next of
'/': repeat c := Next until (c in [#13, #10, #0]);
'*':
begin
while True do
begin
c := Next;
if c = #0 then raise SyntaxError('Unclosed comment');
if c = '*' then if Next = '/' then Break else Back;
end;
end;
else Back; Exit('/');
end;
end
else if c = '#' then repeat c := Next until (c in [#13, #10, #0])
else if (c = #0) or (c > ' ') then Exit(c);
end;
end;
function TJSONTokener.NextValue: IAutoPtr<TObject>;
var c, b: Char; s, sb: string;
begin
c := NextClean;
case c of
'"', ''': Exit(TAutoPtr<TObject>.New(TStringObject.Create(NextString(c))));
'{': begin Back; Exit(TAutoPtr<TObject>.New(TJSONObject.Create(Self))); end;
'[', '(': begin Back; Exit(TAutoPtr<TObject>.New(TJSONArray.Create(Self))); end;
end;
b := c;
while (c >= ' ') and (Pos(c, ',:]}/\"[{;=#') = 0) do
begin sb := sb + c; c := Next; end;
Back;
s := Trim(sb);
if s = '' then raise SyntaxError('Missing value');
if LowerCase(s) = 'true' then Exit(TAutoPtr<TObject>.New(TBooleanObject.TRUE));
if LowerCase(s) = 'false' then Exit(TAutoPtr<TObject>.New(TBooleanObject.FALSE));
if LowerCase(s) = 'null' then Exit(TAutoPtr<TObject>.New(TJSONObject.NULL));
// 數值解析級聯...
Exit(TAutoPtr<TObject>.New(TStringObject.Create(s)));
end;
此實作展示了如何將經典 Java 庫優雅移植至 Delphi,保留原始設計精髓同時發揮 Delphi 語言優勢。