123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- {
- This file is part of the Free Component Library
- JSON source parser
- Copyright (c) 2007 by Michael Van Canneyt [email protected]
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- {$mode objfpc}
- {$h+}
- unit jsonparser;
- interface
- uses
- Classes, SysUtils, fpJSON, jsonscanner;
-
- Type
- { TJSONParser }
- TJSONParser = Class(TObject)
- Private
- FScanner : TJSONScanner;
- function GetO(AIndex: TJSONOption): Boolean;
- function GetOptions: TJSONOptions; inline;
- function ParseNumber: TJSONNumber;
- procedure SetO(AIndex: TJSONOption; AValue: Boolean);
- procedure SetOptions(AValue: TJSONOptions);
- Protected
- procedure DoError(const Msg: String);
- function DoParse(AtCurrent,AllowEOF: Boolean): TJSONData;
- function GetNextToken: TJSONToken;
- function CurrentTokenString: String;
- function CurrentToken: TJSONToken; inline;
- function ParseArray: TJSONArray;
- function ParseObject: TJSONObject;
- Property Scanner : TJSONScanner read FScanner;
- Public
- function Parse: TJSONData;
- Constructor Create(Source : TStream; AUseUTF8 : Boolean = True); overload;deprecated 'use options form instead';
- Constructor Create(Source : TJSONStringType; AUseUTF8 : Boolean = True); overload;deprecated 'use options form instead';
- constructor Create(Source: TStream; AOptions: TJSONOptions); overload;
- constructor Create(const Source: String; AOptions: TJSONOptions); overload;
- destructor Destroy();override;
- // Use strict JSON: " for strings, object members are strings, not identifiers
- Property Strict : Boolean Index joStrict Read GetO Write SetO ; deprecated 'use options instead';
- // if set to TRUE, then strings will be converted to UTF8 ansistrings, not system codepage ansistrings.
- Property UseUTF8 : Boolean index joUTF8 Read GetO Write SetO; deprecated 'Use options instead';
- // Parsing options
- Property Options : TJSONOptions Read GetOptions Write SetOptions;
- end;
-
- EJSONParser = Class(EParserError);
-
- implementation
- Resourcestring
- SErrUnexpectedEOF = 'Unexpected EOF encountered.';
- SErrUnexpectedToken = 'Unexpected token (%s) encountered.';
- SErrExpectedColon = 'Expected colon (:), got token "%s".';
- SErrEmptyElement = 'Empty element encountered.';
- SErrExpectedElementName = 'Expected element name, got token "%s"';
- SExpectedCommaorBraceClose = 'Expected , or ], got token "%s".';
- SErrInvalidNumber = 'Number is not an integer or real number: %s';
- SErrNoScanner = 'No scanner. No source specified ?';
-
- { TJSONParser }
- procedure DefJSONParserHandler(AStream: TStream; const AUseUTF8: Boolean; out
- Data: TJSONData);
- Var
- P : TJSONParser;
- begin
- Data:=Nil;
- P:=TJSONParser.Create(AStream,[joUTF8]);
- try
- Data:=P.Parse;
- finally
- P.Free;
- end;
- end;
- function TJSONParser.Parse: TJSONData;
- begin
- if (FScanner=Nil) then
- DoError(SErrNoScanner);
- Result:=DoParse(False,True);
- end;
- {
- Consume next token and convert to JSON data structure.
- If AtCurrent is true, the current token is used. If false,
- a token is gotten from the scanner.
- If AllowEOF is false, encountering a tkEOF will result in an exception.
- }
- function TJSONParser.CurrentToken: TJSONToken;
- begin
- Result:=FScanner.CurToken;
- end;
- function TJSONParser.CurrentTokenString: String;
- begin
- If CurrentToken in [tkString,tkIdentifier,tkNumber,tkComment] then
- Result:=FScanner.CurTokenString
- else
- Result:=TokenInfos[CurrentToken];
- end;
- function TJSONParser.DoParse(AtCurrent, AllowEOF: Boolean): TJSONData;
- var
- T : TJSONToken;
-
- begin
- Result:=nil;
- try
- If not AtCurrent then
- T:=GetNextToken
- else
- T:=FScanner.CurToken;
- Case T of
- tkEof : If Not AllowEof then
- DoError(SErrUnexpectedEOF);
- tkNull : Result:=CreateJSON;
- tkTrue,
- tkFalse : Result:=CreateJSON(t=tkTrue);
- tkString : if joUTF8 in Options then
- Result:=CreateJSON(UTF8Decode(CurrentTokenString))
- else
- Result:=CreateJSON(CurrentTokenString);
- tkCurlyBraceOpen : Result:=ParseObject;
- tkCurlyBraceClose : DoError(SErrUnexpectedToken);
- tkSQuaredBraceOpen : Result:=ParseArray;
- tkSQuaredBraceClose : DoError(SErrUnexpectedToken);
- tkNumber : Result:=ParseNumber;
- tkComma : DoError(SErrUnexpectedToken);
- tkIdentifier : DoError(SErrUnexpectedToken);
- end;
- except
- FreeAndNil(Result);
- Raise;
- end;
- end;
- // Creates the correct JSON number type, based on the current token.
- function TJSONParser.ParseNumber: TJSONNumber;
- Var
- I : Integer;
- I64 : Int64;
- QW : QWord;
- F : TJSONFloat;
- S : String;
- begin
- S:=CurrentTokenString;
- I:=0;
- if TryStrToQWord(S,QW) then
- begin
- if QW>qword(high(Int64)) then
- Result:=CreateJSON(QW)
- else
- if QW>MaxInt then
- begin
- I64 := QW;
- Result:=CreateJSON(I64);
- end
- else
- begin
- I := QW;
- Result:=CreateJSON(I);
- end
- end
- else
- begin
- If TryStrToInt64(S,I64) then
- if (I64>Maxint) or (I64<-MaxInt) then
- Result:=CreateJSON(I64)
- Else
- begin
- I:=I64;
- Result:=CreateJSON(I);
- end
- else
- begin
- I:=0;
- Val(S,F,I);
- If (I<>0) then
- DoError(SErrInvalidNumber);
- Result:=CreateJSON(F);
- end;
- end;
- end;
- function TJSONParser.GetO(AIndex: TJSONOption): Boolean;
- begin
- Result:=AIndex in Options;
- end;
- function TJSONParser.GetOptions: TJSONOptions;
- begin
- Result:=FScanner.Options
- end;
- procedure TJSONParser.SetO(AIndex: TJSONOption; AValue: Boolean);
- begin
- if aValue then
- FScanner.Options:=FScanner.Options+[AINdex]
- else
- FScanner.Options:=FScanner.Options-[AINdex]
- end;
- procedure TJSONParser.SetOptions(AValue: TJSONOptions);
- begin
- FScanner.Options:=AValue;
- end;
- // Current token is {, on exit current token is }
- function TJSONParser.ParseObject: TJSONObject;
- Var
- T : TJSONtoken;
- E : TJSONData;
- N : String;
- LastComma : Boolean;
- begin
- LastComma:=False;
- Result:=CreateJSONObject([]);
- Try
- T:=GetNextToken;
- While T<>tkCurlyBraceClose do
- begin
- If (T<>tkString) and (T<>tkIdentifier) then
- DoError(SErrExpectedElementName);
- N:=CurrentTokenString;
- T:=GetNextToken;
- If (T<>tkColon) then
- DoError(SErrExpectedColon);
- E:=DoParse(False,False);
- Result.Add(N,E);
- T:=GetNextToken;
- If Not (T in [tkComma,tkCurlyBraceClose]) then
- DoError(SExpectedCommaorBraceClose);
- If T=tkComma then
- begin
- T:=GetNextToken;
- LastComma:=(t=tkCurlyBraceClose);
- end;
- end;
- If LastComma and ((joStrict in Options) or not (joIgnoreTrailingComma in Options)) then // Test for ,} case
- DoError(SErrUnExpectedToken);
- Except
- FreeAndNil(Result);
- Raise;
- end;
- end;
- // Current token is [, on exit current token is ]
- function TJSONParser.ParseArray: TJSONArray;
- Var
- T : TJSONtoken;
- E : TJSONData;
- LastComma : Boolean;
- S : TJSONOPTions;
- begin
- Result:=CreateJSONArray([]);
- LastComma:=False;
- Try
- Repeat
- T:=GetNextToken;
- If (T<>tkSquaredBraceClose) then
- begin
- E:=DoParse(True,False);
- If (E<>Nil) then
- Result.Add(E)
- else if (Result.Count>0) then
- DoError(SErrEmptyElement);
- T:=GetNextToken;
- If Not (T in [tkComma,tkSquaredBraceClose]) then
- DoError(SExpectedCommaorBraceClose);
- LastComma:=(t=TkComma);
- end;
- Until (T=tkSquaredBraceClose);
- S:=Options;
- If LastComma and ((joStrict in S) or not (joIgnoreTrailingComma in S)) then // Test for ,] case
- DoError(SErrUnExpectedToken);
- Except
- FreeAndNil(Result);
- Raise;
- end;
- end;
- // Get next token, discarding whitespace
- function TJSONParser.GetNextToken: TJSONToken;
- begin
- Repeat
- Result:=FScanner.FetchToken;
- Until (Not (Result in [tkComment,tkWhiteSpace]));
- end;
- procedure TJSONParser.DoError(const Msg: String);
- Var
- S : String;
- begin
- S:=Format(Msg,[CurrentTokenString]);
- S:=Format('Error at line %d, Pos %d:',[FScanner.CurRow,FSCanner.CurColumn])+S;
- Raise EJSONParser.Create(S);
- end;
- constructor TJSONParser.Create(Source: TStream; AUseUTF8 : Boolean = True);
- begin
- Inherited Create;
- FScanner:=TJSONScanner.Create(Source,[joUTF8]);
- if AUseUTF8 then
- Options:=Options + [joUTF8];
- end;
- constructor TJSONParser.Create(Source: TJSONStringType; AUseUTF8 : Boolean = True);
- begin
- Inherited Create;
- FScanner:=TJSONScanner.Create(Source,[joUTF8]);
- if AUseUTF8 then
- Options:=Options + [joUTF8];
- end;
- constructor TJSONParser.Create(Source: TStream; AOptions: TJSONOptions);
- begin
- FScanner:=TJSONScanner.Create(Source,AOptions);
- end;
- constructor TJSONParser.Create(const Source: String; AOptions: TJSONOptions);
- begin
- FScanner:=TJSONScanner.Create(Source,AOptions);
- end;
- destructor TJSONParser.Destroy();
- begin
- FreeAndNil(FScanner);
- inherited Destroy();
- end;
- Procedure InitJSONHandler;
- begin
- if GetJSONParserHandler=Nil then
- SetJSONParserHandler(@DefJSONParserHandler);
- end;
- Procedure DoneJSONHandler;
- begin
- if GetJSONParserHandler=@DefJSONParserHandler then
- SetJSONParserHandler(Nil);
- end;
- initialization
- InitJSONHandler;
- finalization
- DoneJSONHandler;
- end.
|