mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-23 08:49:38 +02:00
* Initial implementation
git-svn-id: trunk@8522 -
This commit is contained in:
parent
05092f01f5
commit
f83d92c9a0
9
.gitattributes
vendored
9
.gitattributes
vendored
@ -4262,6 +4262,15 @@ packages/fcl-image/src/pixtools.pp svneol=native#text/plain
|
||||
packages/fcl-image/src/pngcomn.pp svneol=native#text/plain
|
||||
packages/fcl-image/src/pscanvas.pp svneol=native#text/plain
|
||||
packages/fcl-image/src/targacmn.pp svneol=native#text/plain
|
||||
packages/fcl-json/Makefile svneol=native#text/plain
|
||||
packages/fcl-json/Makefile.fpc svneol=native#text/plain
|
||||
packages/fcl-json/src/fpjson.pp svneol=native#text/plain
|
||||
packages/fcl-json/src/jsonparser.pp svneol=native#text/plain
|
||||
packages/fcl-json/src/jsonscanner.pp svneol=native#text/plain
|
||||
packages/fcl-json/tests/testjson.lpi svneol=native#text/plain
|
||||
packages/fcl-json/tests/testjson.pp svneol=native#text/plain
|
||||
packages/fcl-json/tests/testjsondata.pp svneol=native#text/plain
|
||||
packages/fcl-json/tests/testjsonparser.pp svneol=native#text/plain
|
||||
packages/fcl-net/Makefile svneol=native#text/plain
|
||||
packages/fcl-net/Makefile.fpc svneol=native#text/plain
|
||||
packages/fcl-net/src/fpmake.inc svneol=native#text/plain
|
||||
|
2783
packages/fcl-json/Makefile
Normal file
2783
packages/fcl-json/Makefile
Normal file
File diff suppressed because it is too large
Load Diff
29
packages/fcl-json/Makefile.fpc
Normal file
29
packages/fcl-json/Makefile.fpc
Normal file
@ -0,0 +1,29 @@
|
||||
#
|
||||
# Makefile.fpc for XML for FCL
|
||||
#
|
||||
|
||||
[package]
|
||||
name=fcl-json
|
||||
version=2.2.0
|
||||
|
||||
[target]
|
||||
units=fpjson jsonscanner jsonparser
|
||||
rsts=fpjson jsonscanner jsonparser
|
||||
|
||||
[require]
|
||||
packages=fcl-base
|
||||
|
||||
[compiler]
|
||||
options=-S2h
|
||||
sourcedir=src
|
||||
|
||||
[install]
|
||||
fpcpackage=y
|
||||
|
||||
[default]
|
||||
fpcdir=../..
|
||||
|
||||
[rules]
|
||||
.NOTPARALLEL:
|
||||
|
||||
jsonparser$(PPUEXT): jsonparser.pp fpjson$(PPUEXT) jsonscanner$(PPUEXT)
|
1448
packages/fcl-json/src/fpjson.pp
Normal file
1448
packages/fcl-json/src/fpjson.pp
Normal file
File diff suppressed because it is too large
Load Diff
237
packages/fcl-json/src/jsonparser.pp
Normal file
237
packages/fcl-json/src/jsonparser.pp
Normal file
@ -0,0 +1,237 @@
|
||||
unit jsonparser;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, fpJSON, jsonscanner;
|
||||
|
||||
Type
|
||||
|
||||
{ TJSONParser }
|
||||
|
||||
TJSONParser = Class(TObject)
|
||||
Private
|
||||
FScanner : TJSONScanner;
|
||||
function ParseNumber: TJSONNumber;
|
||||
Protected
|
||||
procedure DoError(Msg: String);
|
||||
function DoParse(AtCurrent,AllowEOF: Boolean): TJSONData;
|
||||
function GetNextToken: TJSONToken;
|
||||
function CurrentTokenString: String;
|
||||
function CurrentToken: TJSONToken;
|
||||
function ParseArray: TJSONArray;
|
||||
function ParseObject: TJSONObject;
|
||||
Public
|
||||
function Parse: TJSONData;
|
||||
Constructor Create(Source : TStream); overload;
|
||||
Constructor Create(Source : TJSONStringType); overload;
|
||||
end;
|
||||
|
||||
EJSONScanner = Class(Exception);
|
||||
|
||||
implementation
|
||||
|
||||
Resourcestring
|
||||
SErrUnexpectedEOF = 'Unexpected EOF encountered.';
|
||||
SErrUnexpectedToken = 'Unexpected token (%s) encountered.';
|
||||
SErrExpectedColon = 'Expected colon (:), got token "%s".';
|
||||
SErrUnexpectedComma = 'Invalid comma encountered.';
|
||||
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';
|
||||
|
||||
{ TJSONParser }
|
||||
|
||||
Function TJSONParser.Parse : TJSONData;
|
||||
|
||||
begin
|
||||
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,tkNumber] 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:=TJSONNull.Create;
|
||||
tkTrue,
|
||||
tkFalse : Result:=TJSONBoolean.Create(t=tkTrue);
|
||||
tkString : Result:=TJSONString.Create(CurrentTokenString);
|
||||
tkCurlyBraceOpen : Result:=ParseObject;
|
||||
tkCurlyBraceClose : DoError(SErrUnexpectedToken);
|
||||
tkSQuaredBraceOpen : Result:=ParseArray;
|
||||
tkSQuaredBraceClose : DoError(SErrUnexpectedToken);
|
||||
tkNumber : Result:=ParseNumber;
|
||||
tkComma : DoError(SErrUnexpectedToken);
|
||||
end;
|
||||
except
|
||||
if assigned(Result) then
|
||||
FreeAndNil(Result);
|
||||
Raise;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
// Creates the correct JSON number type, based on the current token.
|
||||
Function TJSONParser.ParseNumber : TJSONNumber;
|
||||
|
||||
Var
|
||||
I : Integer;
|
||||
F : TJSONFloat;
|
||||
S : String;
|
||||
|
||||
begin
|
||||
S:=CurrentTokenString;
|
||||
I:=0;
|
||||
If TryStrToInt(S,I) then
|
||||
Result:=TJSONIntegerNumber.Create(I)
|
||||
else
|
||||
begin
|
||||
I:=0;
|
||||
Val(S,F,I);
|
||||
If (I<>0) then
|
||||
DoError(SErrInvalidNumber);
|
||||
Result:=TJSONFloatNumber.Create(F);
|
||||
end;
|
||||
end;
|
||||
|
||||
// Current token is {, on exit current token is }
|
||||
Function TJSONParser.ParseObject : TJSONObject;
|
||||
|
||||
Var
|
||||
T : TJSONtoken;
|
||||
E : TJSONData;
|
||||
N : String;
|
||||
|
||||
begin
|
||||
Result:=TJSONObject.Create;
|
||||
Try
|
||||
T:=GetNextToken;
|
||||
While T<>tkCurlyBraceClose do
|
||||
begin
|
||||
If T<>tkString then
|
||||
DoError(SErrExpectedElementName);
|
||||
N:=CurrentTokenString;
|
||||
T:=GetNextToken;
|
||||
If (T<>tkColon) then
|
||||
DoError(SErrExpectedColon);
|
||||
Writeln('Getting element');
|
||||
E:=DoParse(False,False);
|
||||
Result.Add(N,E);
|
||||
T:=GetNextToken;
|
||||
If Not (T in [tkComma,tkCurlyBraceClose]) then
|
||||
DoError(SExpectedCommaorBraceClose);
|
||||
If T=tkComma then
|
||||
T:=GetNextToken;
|
||||
end;
|
||||
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;
|
||||
|
||||
begin
|
||||
Result:=TJSONArray.Create;
|
||||
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);
|
||||
If LastComma 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 (Result<>tkWhiteSpace);
|
||||
Writeln('GetNextToken : ',CurrentTokenString);
|
||||
end;
|
||||
|
||||
Procedure TJSONParser.DoError(Msg : String);
|
||||
|
||||
Var
|
||||
S : String;
|
||||
|
||||
begin
|
||||
S:=Format(Msg,[CurrentTokenString]);
|
||||
S:=Format('Error at line %d, Pos %d:',[FScanner.CurRow,FSCanner.CurColumn])+S;
|
||||
Raise EJSONScanner.Create(S);
|
||||
end;
|
||||
|
||||
constructor TJSONParser.Create(Source: TStream);
|
||||
begin
|
||||
Inherited Create;
|
||||
FScanner:=TJSONScanner.Create(Source);
|
||||
end;
|
||||
|
||||
constructor TJSONParser.Create(Source: TJSONStringType);
|
||||
begin
|
||||
Inherited Create;
|
||||
FScanner:=TJSONScanner.Create(Source);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
355
packages/fcl-json/src/jsonscanner.pp
Normal file
355
packages/fcl-json/src/jsonscanner.pp
Normal file
@ -0,0 +1,355 @@
|
||||
{
|
||||
This file is part of the Free Component Library
|
||||
|
||||
JSON source lexical scanner
|
||||
Copyright (c) 2007 by Michael Van Canneyt michael@freepascal.org
|
||||
|
||||
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 jsonscanner;
|
||||
|
||||
interface
|
||||
|
||||
uses SysUtils, Classes;
|
||||
|
||||
resourcestring
|
||||
SErrInvalidCharacter = 'Invalid character ''%s''';
|
||||
SErrOpenString = 'string exceeds end of line';
|
||||
|
||||
type
|
||||
|
||||
TJSONToken = (
|
||||
tkEOF,
|
||||
tkWhitespace,
|
||||
tkString,
|
||||
tkNumber,
|
||||
tkTrue,
|
||||
tkFalse,
|
||||
tkNull,
|
||||
// Simple (one-character) tokens
|
||||
tkComma, // ','
|
||||
tkColon, // ':'
|
||||
tkCurlyBraceOpen, // '{'
|
||||
tkCurlyBraceClose, // '}'
|
||||
tkSquaredBraceOpen, // '['
|
||||
tkSquaredBraceClose, // ']'
|
||||
tkUnknown
|
||||
);
|
||||
|
||||
EScannerError = class(Exception);
|
||||
|
||||
|
||||
TJSONScanner = class
|
||||
private
|
||||
FSource : TStringList;
|
||||
FCurRow: Integer;
|
||||
FCurToken: TJSONToken;
|
||||
FCurTokenString: string;
|
||||
FCurLine: string;
|
||||
TokenStr: PChar;
|
||||
function GetCurColumn: Integer;
|
||||
protected
|
||||
procedure Error(const Msg: string);overload;
|
||||
procedure Error(const Msg: string; Args: array of Const);overload;
|
||||
function DoFetchToken: TJSONToken;
|
||||
public
|
||||
constructor Create(Source : TStream); overload;
|
||||
constructor Create(Source : String); overload;
|
||||
destructor Destroy; override;
|
||||
function FetchToken: TJSONToken;
|
||||
|
||||
|
||||
property CurLine: string read FCurLine;
|
||||
property CurRow: Integer read FCurRow;
|
||||
property CurColumn: Integer read GetCurColumn;
|
||||
|
||||
property CurToken: TJSONToken read FCurToken;
|
||||
property CurTokenString: string read FCurTokenString;
|
||||
end;
|
||||
|
||||
const
|
||||
TokenInfos: array[TJSONToken] of string = (
|
||||
'EOF',
|
||||
'Whitespace',
|
||||
'String',
|
||||
'Number',
|
||||
'True',
|
||||
'False',
|
||||
'Null',
|
||||
',',
|
||||
':',
|
||||
'{',
|
||||
'}',
|
||||
'[',
|
||||
']',
|
||||
''
|
||||
);
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
constructor TJSONScanner.Create(Source : TStream);
|
||||
|
||||
begin
|
||||
FSource:=TStringList.Create;
|
||||
FSource.LoadFromStream(Source);
|
||||
end;
|
||||
|
||||
constructor TJSONScanner.Create(Source : String);
|
||||
begin
|
||||
FSource:=TStringList.Create;
|
||||
FSource.Text:=Source;
|
||||
end;
|
||||
|
||||
destructor TJSONScanner.Destroy;
|
||||
begin
|
||||
FreeAndNil(FSource);
|
||||
Inherited;
|
||||
end;
|
||||
|
||||
|
||||
function TJSONScanner.FetchToken: TJSONToken;
|
||||
|
||||
begin
|
||||
Result:=DoFetchToken;
|
||||
end;
|
||||
|
||||
procedure TJSONScanner.Error(const Msg: string);
|
||||
begin
|
||||
raise EScannerError.Create(Msg);
|
||||
end;
|
||||
|
||||
procedure TJSONScanner.Error(const Msg: string; Args: array of Const);
|
||||
begin
|
||||
raise EScannerError.CreateFmt(Msg, Args);
|
||||
end;
|
||||
|
||||
function TJSONScanner.DoFetchToken: TJSONToken;
|
||||
|
||||
function FetchLine: Boolean;
|
||||
begin
|
||||
Result:=FCurRow<FSource.Count;
|
||||
if Result then
|
||||
begin
|
||||
FCurLine:=FSource[FCurRow];
|
||||
TokenStr:=PChar(FCurLine);
|
||||
Inc(FCurRow);
|
||||
end
|
||||
else
|
||||
begin
|
||||
FCurLine:='';
|
||||
TokenStr:=nil;
|
||||
end;
|
||||
end;
|
||||
|
||||
var
|
||||
TokenStart, CurPos: PChar;
|
||||
it : TJSONToken;
|
||||
I : Integer;
|
||||
OldLength, SectionLength, Index: Integer;
|
||||
S : String;
|
||||
|
||||
begin
|
||||
if TokenStr = nil then
|
||||
if not FetchLine then
|
||||
begin
|
||||
Result := tkEOF;
|
||||
FCurToken := Result;
|
||||
exit;
|
||||
end;
|
||||
|
||||
FCurTokenString := '';
|
||||
|
||||
case TokenStr[0] of
|
||||
#0: // Empty line
|
||||
begin
|
||||
FetchLine;
|
||||
Result := tkWhitespace;
|
||||
end;
|
||||
#9, ' ':
|
||||
begin
|
||||
Result := tkWhitespace;
|
||||
repeat
|
||||
Inc(TokenStr);
|
||||
if TokenStr[0] = #0 then
|
||||
if not FetchLine then
|
||||
begin
|
||||
FCurToken := Result;
|
||||
exit;
|
||||
end;
|
||||
until not (TokenStr[0] in [#9, ' ']);
|
||||
end;
|
||||
'"':
|
||||
begin
|
||||
Inc(TokenStr);
|
||||
TokenStart := TokenStr;
|
||||
OldLength := 0;
|
||||
FCurTokenString := '';
|
||||
|
||||
while not (TokenStr[0] in [#0,'"']) do
|
||||
begin
|
||||
if (TokenStr[0]='\') then
|
||||
begin
|
||||
// Save length
|
||||
SectionLength := TokenStr - TokenStart;
|
||||
Inc(TokenStr);
|
||||
// Read escaped token
|
||||
Case TokenStr[0] of
|
||||
'"' : S:='"';
|
||||
't' : S:=#9;
|
||||
'b' : S:=#8;
|
||||
'n' : S:=#10;
|
||||
'r' : S:=#13;
|
||||
'f' : S:=#12;
|
||||
'\' : S:='\';
|
||||
'/' : S:='/';
|
||||
'u' : begin
|
||||
S:='0000';
|
||||
For I:=1 to 4 do
|
||||
begin
|
||||
Inc(TokenStr);
|
||||
Case TokenStr[0] of
|
||||
'0'..'9','A'..'F','a'..'f' :
|
||||
S[i]:=Upcase(TokenStr[0]);
|
||||
else
|
||||
Error(SErrInvalidCharacter, [TokenStr[0]]);
|
||||
end;
|
||||
end;
|
||||
// Takes care of conversion...
|
||||
S:=WideChar(StrToInt('$'+S));
|
||||
end;
|
||||
#0 : Error(SErrOpenString);
|
||||
else
|
||||
Error(SErrInvalidCharacter, [TokenStr[0]]);
|
||||
end;
|
||||
SetLength(FCurTokenString, OldLength + SectionLength+1+Length(S));
|
||||
if SectionLength > 0 then
|
||||
Move(TokenStart^, FCurTokenString[OldLength + 1], SectionLength);
|
||||
Move(S[1],FCurTokenString[OldLength + SectionLength+1],Length(S));
|
||||
Inc(OldLength, SectionLength+Length(S));
|
||||
// Next char
|
||||
// Inc(TokenStr);
|
||||
TokenStart := TokenStr+1;
|
||||
end;
|
||||
if TokenStr[0] = #0 then
|
||||
Error(SErrOpenString);
|
||||
Inc(TokenStr);
|
||||
end;
|
||||
if TokenStr[0] = #0 then
|
||||
Error(SErrOpenString);
|
||||
SectionLength := TokenStr - TokenStart;
|
||||
SetLength(FCurTokenString, OldLength + SectionLength);
|
||||
if SectionLength > 0 then
|
||||
Move(TokenStart^, FCurTokenString[OldLength + 1], SectionLength);
|
||||
Inc(TokenStr);
|
||||
Result := tkString;
|
||||
end;
|
||||
',':
|
||||
begin
|
||||
Inc(TokenStr);
|
||||
Result := tkComma;
|
||||
end;
|
||||
'0'..'9','-':
|
||||
begin
|
||||
TokenStart := TokenStr;
|
||||
while true do
|
||||
begin
|
||||
Inc(TokenStr);
|
||||
case TokenStr[0] of
|
||||
'.':
|
||||
begin
|
||||
if TokenStr[1] in ['0'..'9', 'e', 'E'] then
|
||||
begin
|
||||
Inc(TokenStr);
|
||||
repeat
|
||||
Inc(TokenStr);
|
||||
until not (TokenStr[0] in ['0'..'9', 'e', 'E','-','+']);
|
||||
end;
|
||||
break;
|
||||
end;
|
||||
'0'..'9': ;
|
||||
'e', 'E':
|
||||
begin
|
||||
Inc(TokenStr);
|
||||
if TokenStr[0] in ['-','+'] then
|
||||
Inc(TokenStr);
|
||||
while TokenStr[0] in ['0'..'9'] do
|
||||
Inc(TokenStr);
|
||||
break;
|
||||
end;
|
||||
else
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
SectionLength := TokenStr - TokenStart;
|
||||
SetLength(FCurTokenString, SectionLength);
|
||||
if SectionLength > 0 then
|
||||
Move(TokenStart^, FCurTokenString[1], SectionLength);
|
||||
Result := tkNumber;
|
||||
end;
|
||||
':':
|
||||
begin
|
||||
Inc(TokenStr);
|
||||
Result := tkColon;
|
||||
end;
|
||||
'{':
|
||||
begin
|
||||
Inc(TokenStr);
|
||||
Result := tkCurlyBraceOpen;
|
||||
end;
|
||||
'}':
|
||||
begin
|
||||
Inc(TokenStr);
|
||||
Result := tkCurlyBraceClose;
|
||||
end;
|
||||
'[':
|
||||
begin
|
||||
Inc(TokenStr);
|
||||
Result := tkSquaredBraceOpen;
|
||||
end;
|
||||
']':
|
||||
begin
|
||||
Inc(TokenStr);
|
||||
Result := tkSquaredBraceClose;
|
||||
end;
|
||||
'T','t','F','f','N','n' :
|
||||
begin
|
||||
TokenStart := TokenStr;
|
||||
repeat
|
||||
Inc(TokenStr);
|
||||
until not (TokenStr[0] in ['A'..'Z', 'a'..'z', '0'..'9', '_']);
|
||||
SectionLength := TokenStr - TokenStart;
|
||||
SetLength(FCurTokenString, SectionLength);
|
||||
if SectionLength > 0 then
|
||||
Move(TokenStart^, FCurTokenString[1], SectionLength);
|
||||
for it := tkTrue to tkNull do
|
||||
if CompareText(CurTokenString, TokenInfos[it]) = 0 then
|
||||
begin
|
||||
Result := it;
|
||||
FCurToken := Result;
|
||||
exit;
|
||||
end;
|
||||
Error(SErrInvalidCharacter, [TokenStart[0]]);
|
||||
end;
|
||||
else
|
||||
Error(SErrInvalidCharacter, [TokenStr[0]]);
|
||||
end;
|
||||
|
||||
FCurToken := Result;
|
||||
end;
|
||||
|
||||
function TJSONScanner.GetCurColumn: Integer;
|
||||
begin
|
||||
Result := TokenStr - PChar(CurLine);
|
||||
end;
|
||||
|
||||
end.
|
65
packages/fcl-json/tests/testjson.lpi
Normal file
65
packages/fcl-json/tests/testjson.lpi
Normal file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0"?>
|
||||
<CONFIG>
|
||||
<ProjectOptions>
|
||||
<PathDelim Value="/"/>
|
||||
<Version Value="5"/>
|
||||
<General>
|
||||
<SessionStorage Value="InProjectDir"/>
|
||||
<MainUnit Value="0"/>
|
||||
<IconPath Value="./"/>
|
||||
<TargetFileExt Value=""/>
|
||||
</General>
|
||||
<VersionInfo>
|
||||
<ProjectVersion Value=""/>
|
||||
</VersionInfo>
|
||||
<PublishOptions>
|
||||
<Version Value="2"/>
|
||||
<IgnoreBinaries Value="False"/>
|
||||
<IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
|
||||
<ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
|
||||
</PublishOptions>
|
||||
<RunParams>
|
||||
<local>
|
||||
<FormatVersion Value="1"/>
|
||||
<LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
|
||||
</local>
|
||||
</RunParams>
|
||||
<RequiredPackages Count="3">
|
||||
<Item1>
|
||||
<PackageName Value="LCL"/>
|
||||
</Item1>
|
||||
<Item2>
|
||||
<PackageName Value="FPCUnitTestRunner"/>
|
||||
</Item2>
|
||||
<Item3>
|
||||
<PackageName Value="FCL"/>
|
||||
</Item3>
|
||||
</RequiredPackages>
|
||||
<Units Count="3">
|
||||
<Unit0>
|
||||
<Filename Value="testjson.pp"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="testjson"/>
|
||||
</Unit0>
|
||||
<Unit1>
|
||||
<Filename Value="testjsonparser.pp"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="testjsonparser"/>
|
||||
</Unit1>
|
||||
<Unit2>
|
||||
<Filename Value="testjsondata.pp"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="testjsondata"/>
|
||||
</Unit2>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
<Version Value="5"/>
|
||||
<CodeGeneration>
|
||||
<Generate Value="Faster"/>
|
||||
</CodeGeneration>
|
||||
<Other>
|
||||
<CompilerPath Value="$(CompPath)"/>
|
||||
</Other>
|
||||
</CompilerOptions>
|
||||
</CONFIG>
|
21
packages/fcl-json/tests/testjson.pp
Normal file
21
packages/fcl-json/tests/testjson.pp
Normal file
@ -0,0 +1,21 @@
|
||||
program testjson;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
uses
|
||||
Classes, consoletestrunner, testjsondata, testjsonparser;
|
||||
type
|
||||
{ TLazTestRunner }
|
||||
TMyTestRunner = class(TTestRunner)
|
||||
protected
|
||||
// override the protected methods of TTestRunner to customize its behavior
|
||||
end;
|
||||
|
||||
var
|
||||
Application: TMyTestRunner;
|
||||
begin
|
||||
Application := TMyTestRunner.Create(nil);
|
||||
Application.Initialize;
|
||||
Application.Run;
|
||||
Application.Free;
|
||||
end.
|
1786
packages/fcl-json/tests/testjsondata.pp
Normal file
1786
packages/fcl-json/tests/testjsondata.pp
Normal file
File diff suppressed because it is too large
Load Diff
364
packages/fcl-json/tests/testjsonparser.pp
Normal file
364
packages/fcl-json/tests/testjsonparser.pp
Normal file
@ -0,0 +1,364 @@
|
||||
unit testjsonparser;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, fpcunit, testutils, testregistry,fpjson,
|
||||
jsonParser,testjsondata;
|
||||
|
||||
type
|
||||
|
||||
{ TTestParser }
|
||||
|
||||
TTestParser= class(TTestJSON)
|
||||
private
|
||||
procedure DoTestError(S: String);
|
||||
procedure DoTestFloat(F: TJSONFloat); overload;
|
||||
procedure DoTestFloat(F: TJSONFloat; S: String); overload;
|
||||
procedure DoTestObject(S: String; const ElNames: array of String);
|
||||
procedure DoTestString(S : String);
|
||||
procedure DoTestArray(S: String; ACount: Integer);
|
||||
published
|
||||
procedure TestEmpty;
|
||||
procedure TestNull;
|
||||
procedure TestTrue;
|
||||
procedure TestFalse;
|
||||
procedure TestFloat;
|
||||
procedure TestInteger;
|
||||
procedure TestString;
|
||||
procedure TestArray;
|
||||
procedure TestObject;
|
||||
procedure TestMixed;
|
||||
procedure TestErrors;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
procedure TTestParser.TestEmpty;
|
||||
|
||||
Var
|
||||
P : TJSONParser;
|
||||
J : TJSONData;
|
||||
|
||||
begin
|
||||
P:=TJSONParser.Create('');
|
||||
Try
|
||||
J:=P.Parse;
|
||||
If (J<>Nil) then
|
||||
Fail('Empty returns Nil');
|
||||
Finally
|
||||
FreeAndNil(J);
|
||||
FreeAndNil(P);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTestParser.TestInteger;
|
||||
|
||||
Var
|
||||
P : TJSONParser;
|
||||
J : TJSONData;
|
||||
|
||||
begin
|
||||
P:=TJSONParser.Create('1');
|
||||
Try
|
||||
J:=P.Parse;
|
||||
If (J=Nil) then
|
||||
Fail('Parse of 1 fails');
|
||||
TestJSONType(J,jtNumber);
|
||||
TestAsInteger(J,1);
|
||||
Finally
|
||||
FreeAndNil(J);
|
||||
FreeAndNil(P);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTestParser.TestNull;
|
||||
|
||||
Var
|
||||
P : TJSONParser;
|
||||
J : TJSONData;
|
||||
|
||||
begin
|
||||
P:=TJSONParser.Create('Null');
|
||||
Try
|
||||
J:=P.Parse;
|
||||
If (J=Nil) then
|
||||
Fail('Parse of Null fails');
|
||||
TestJSONType(J,jtNull);
|
||||
Finally
|
||||
FreeAndNil(J);
|
||||
FreeAndNil(P);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTestParser.TestTrue;
|
||||
|
||||
Var
|
||||
P : TJSONParser;
|
||||
J : TJSONData;
|
||||
|
||||
begin
|
||||
P:=TJSONParser.Create('True');
|
||||
Try
|
||||
J:=P.Parse;
|
||||
If (J=Nil) then
|
||||
Fail('Parse of True fails');
|
||||
TestJSONType(J,jtBoolean);
|
||||
TestAsBoolean(J,True);
|
||||
Finally
|
||||
FreeAndNil(J);
|
||||
FreeAndNil(P);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTestParser.TestFalse;
|
||||
|
||||
Var
|
||||
P : TJSONParser;
|
||||
J : TJSONData;
|
||||
|
||||
begin
|
||||
P:=TJSONParser.Create('False');
|
||||
Try
|
||||
J:=P.Parse;
|
||||
If (J=Nil) then
|
||||
Fail('Parse of False fails');
|
||||
TestJSONType(J,jtBoolean);
|
||||
TestAsBoolean(J,False);
|
||||
Finally
|
||||
FreeAndNil(J);
|
||||
FreeAndNil(P);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTestParser.TestFloat;
|
||||
|
||||
|
||||
begin
|
||||
DoTestFloat(1.2);
|
||||
DoTestFloat(-1.2);
|
||||
DoTestFloat(0);
|
||||
DoTestFloat(1.2e1);
|
||||
DoTestFloat(-1.2e1);
|
||||
DoTestFloat(0);
|
||||
DoTestFloat(1.2,'1.2');
|
||||
DoTestFloat(-1.2,'-1.2');
|
||||
DoTestFloat(0,'0.0');
|
||||
end;
|
||||
|
||||
procedure TTestParser.TestString;
|
||||
|
||||
begin
|
||||
DoTestString('A string');
|
||||
DoTestString('');
|
||||
DoTestString('\"');
|
||||
end;
|
||||
|
||||
|
||||
procedure TTestParser.TestArray;
|
||||
|
||||
Var
|
||||
S1,S2,S3 : String;
|
||||
|
||||
|
||||
begin
|
||||
DoTestArray('[]',0);
|
||||
DoTestArray('[Null]',1);
|
||||
DoTestArray('[True]',1);
|
||||
DoTestArray('[False]',1);
|
||||
DoTestArray('[1]',1);
|
||||
DoTestArray('[1, 2]',2);
|
||||
DoTestArray('[1, 2, 3]',3);
|
||||
Str(1.2,S1);
|
||||
Str(2.3,S2);
|
||||
Str(3.4,S3);
|
||||
DoTestArray('['+S1+']',1);
|
||||
DoTestArray('['+S1+', '+S2+']',2);
|
||||
DoTestArray('['+S1+', '+S2+', '+S3+']',3);
|
||||
DoTestArray('["A string"]',1);
|
||||
DoTestArray('["A string", "Another string"]',2);
|
||||
DoTestArray('["A string", "Another string", "Yet another string"]',3);
|
||||
DoTestArray('[Null, False]',2);
|
||||
DoTestArray('[True, False]',2);
|
||||
DoTestArray('[Null, 1]',2);
|
||||
DoTestArray('[1, "A string"]',2);
|
||||
DoTestArray('[1, []]',2);
|
||||
DoTestArray('[1, [1, 2]]',2);
|
||||
end;
|
||||
|
||||
procedure TTestParser.TestMixed;
|
||||
|
||||
begin
|
||||
DoTestArray('[1, {}]',2);
|
||||
DoTestArray('[1, { "a" : 1 }]',2);
|
||||
DoTestArray('[1, { "a" : 1 }, 1]',3);
|
||||
DoTestObject('{ "a" : [1, 2] }',['a']);
|
||||
DoTestObject('{ "a" : [1, 2], "B" : { "c" : "d" } }',['a','B']);
|
||||
end;
|
||||
|
||||
procedure TTestParser.TestObject;
|
||||
begin
|
||||
DoTestObject('{}',[]);
|
||||
DoTestObject('{ "a" : 1 }',['a']);
|
||||
DoTestObject('{ "a" : 1, "B" : "String" }',['a','B']);
|
||||
DoTestObject('{ "a" : 1, "B" : {} }',['a','B']);
|
||||
DoTestObject('{ "a" : 1, "B" : { "c" : "d" } }',['a','B']);
|
||||
end;
|
||||
|
||||
|
||||
procedure TTestParser.DoTestObject(S : String; Const ElNames : Array of String);
|
||||
|
||||
Var
|
||||
P : TJSONParser;
|
||||
J : TJSONData;
|
||||
O : TJSONObject;
|
||||
I : Integer;
|
||||
|
||||
begin
|
||||
P:=TJSONParser.Create(S);
|
||||
Try
|
||||
J:=P.Parse;
|
||||
If (J=Nil) then
|
||||
Fail('Parse of object "'+S+'" fails');
|
||||
TestJSONType(J,jtObject);
|
||||
TestItemCount(J,High(ElNames)-Low(ElNames)+1);
|
||||
O:=TJSONObject(J);
|
||||
For I:=Low(ElNames) to High(ElNames) do
|
||||
AssertEquals(Format('Element %d name',[I-Low(Elnames)])
|
||||
,ElNames[i], O.Names[I-Low(ElNames)]);
|
||||
TestJSON(J,S);
|
||||
Finally
|
||||
FreeAndNil(J);
|
||||
FreeAndNil(P);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TTestParser.DoTestArray(S : String; ACount : Integer);
|
||||
|
||||
Var
|
||||
P : TJSONParser;
|
||||
J : TJSONData;
|
||||
|
||||
begin
|
||||
P:=TJSONParser.Create(S);
|
||||
Try
|
||||
J:=P.Parse;
|
||||
If (J=Nil) then
|
||||
Fail('Parse of array "'+S+'" fails');
|
||||
TestJSONType(J,jtArray);
|
||||
TestItemCount(J,ACount);
|
||||
TestJSON(J,S);
|
||||
Finally
|
||||
FreeAndNil(J);
|
||||
FreeAndNil(P);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTestParser.TestErrors;
|
||||
|
||||
begin
|
||||
DoTestError('a');
|
||||
DoTestError('"b');
|
||||
DoTestError('1Tru');
|
||||
DoTestError('b"');
|
||||
DoTestError('{"a" : }');
|
||||
DoTestError('{"a" : ""');
|
||||
DoTestError('{"a : ""');
|
||||
DoTestError('[1,]');
|
||||
DoTestError('[,]');
|
||||
DoTestError('[,,]');
|
||||
DoTestError('[1,,]');
|
||||
end;
|
||||
|
||||
procedure TTestParser.DoTestError(S : String);
|
||||
|
||||
Var
|
||||
P : TJSONParser;
|
||||
J : TJSONData;
|
||||
ParseOK : Boolean;
|
||||
N : String;
|
||||
|
||||
begin
|
||||
ParseOK:=False;
|
||||
P:=TJSONParser.Create(S);
|
||||
J:=Nil;
|
||||
Try
|
||||
Try
|
||||
Repeat
|
||||
FreeAndNil(J);
|
||||
J:=P.Parse;
|
||||
ParseOK:=True;
|
||||
If (J<>Nil) then
|
||||
N:=J.ClassName;
|
||||
Until (J=Nil)
|
||||
Finally
|
||||
FreeAndNil(J);
|
||||
FreeAndNil(P);
|
||||
end;
|
||||
except
|
||||
ParseOk:=False;
|
||||
end;
|
||||
If ParseOK then
|
||||
Fail('Parse of JSON string "'+S+'" should fail, but returned '+N);
|
||||
end;
|
||||
|
||||
procedure TTestParser.DoTestString(S: String);
|
||||
|
||||
Var
|
||||
P : TJSONParser;
|
||||
J : TJSONData;
|
||||
|
||||
begin
|
||||
P:=TJSONParser.Create('"'+S+'"');
|
||||
Try
|
||||
J:=P.Parse;
|
||||
If (J=Nil) then
|
||||
Fail('Parse of string "'+S+'" fails');
|
||||
TestJSONType(J,jtString);
|
||||
TestAsString(J,JSONStringToString(S));
|
||||
TestJSON(J,'"'+S+'"');
|
||||
Finally
|
||||
FreeAndNil(J);
|
||||
FreeAndNil(P);
|
||||
end;
|
||||
|
||||
end;
|
||||
|
||||
procedure TTestParser.DoTestFloat(F : TJSONFloat);
|
||||
|
||||
Var
|
||||
S : String;
|
||||
|
||||
begin
|
||||
Str(F,S);
|
||||
DoTestFloat(F,S);
|
||||
end;
|
||||
|
||||
procedure TTestParser.DoTestFloat(F : TJSONFloat; S : String);
|
||||
|
||||
Var
|
||||
P : TJSONParser;
|
||||
J : TJSONData;
|
||||
|
||||
begin
|
||||
P:=TJSONParser.Create(S);
|
||||
Try
|
||||
J:=P.Parse;
|
||||
If (J=Nil) then
|
||||
Fail('Parse of float '+S+' fails');
|
||||
TestJSONType(J,jtNumber);
|
||||
TestAsFloat(J,F);
|
||||
Finally
|
||||
FreeAndNil(J);
|
||||
FreeAndNil(P);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
initialization
|
||||
RegisterTest(TTestParser);
|
||||
end.
|
||||
|
Loading…
Reference in New Issue
Block a user