mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-20 11:09:42 +02:00
* JSON Enumerator support
git-svn-id: trunk@25694 -
This commit is contained in:
parent
e977c09098
commit
cfaf45c7da
@ -112,13 +112,48 @@ forms:
|
||||
Where the type of AVAlue is one of the supported types:
|
||||
integer, int64, double, string, TJSONArray or TJSONObject.
|
||||
|
||||
The Delete() call deletes an element from an array or object.
|
||||
The Delete() call deletes an element from an array or object. The element is
|
||||
freed.
|
||||
|
||||
Important remark:
|
||||
The array and object classes own their members: the members are destroyed as
|
||||
they are deleted. For this, the Extract() call exists: it removes an
|
||||
element/member from the array/object, without destroying it.
|
||||
|
||||
Converting from string/stream to JSONData
|
||||
=========================================
|
||||
|
||||
The fpjson unit contains a GetJSON() function which accepts a string or a
|
||||
stream as a parameter. The function will parse the JSON in the stream and
|
||||
the return value is a TJSONData value corresponding to the JSON.
|
||||
The function works with a callback, which is set by the JSONParser unit.
|
||||
The JSONParser unit simply needs to be included in the project.
|
||||
|
||||
The parsing happens with default settings for the parser class.
|
||||
You can override this behaviour by creating your own callback,
|
||||
and creating the parser with different settings.
|
||||
|
||||
Enumerator support
|
||||
==================
|
||||
|
||||
the TJSONData class offers support for an enumerator, hence the
|
||||
For e in JSON do
|
||||
construct can be used. The enumerator is a TJSONEnum value, which has 3
|
||||
members:
|
||||
Key : The key of the element
|
||||
(name in TJSONObject, Index in TJSONArray, empty otherwise)
|
||||
KeyNum: The index of the element.
|
||||
(Index in TJSONArray/TJSONObject, 0 otherwise)
|
||||
Value : The value of the element
|
||||
(These are the member values for TJSONArray/TJSONObject, and is the
|
||||
element itself otherwise)
|
||||
|
||||
While the enumerator is looping, it is not allowed to change the content of
|
||||
the array or object, and the value may not be freed.
|
||||
|
||||
Scanner/Parser
|
||||
==============
|
||||
|
||||
The JSONSCanner unit contains a scanner for JSON data: TJSONScanner.
|
||||
Currently it does not support full unicode, only UTF-8 is supported.
|
||||
|
||||
@ -165,3 +200,30 @@ A second effect of the Strict property is the requirement of " as a string
|
||||
delimiter. A single quote is also often found in Javascript and JSON:
|
||||
{ title: 'A nice title' }
|
||||
By default, this is accepted. Setting 'Strict' to true will reject this.
|
||||
|
||||
Customizing the classes : Factory support
|
||||
=========================================
|
||||
|
||||
The various classes created by the methods can be customized.
|
||||
This can be useful to create customized descendents, for example to attach
|
||||
extra data to the various values. All instances of TJSONData are created
|
||||
through the CreateJSON() functions, which use a set of customizable classes
|
||||
to create the JSONData structures.
|
||||
|
||||
All functions which somehow create a new instance (clone, add, insert, parsing)
|
||||
use the CreateJSON functions.
|
||||
|
||||
Which classes need to be created for a specific value is enumerated in
|
||||
|
||||
TJSONInstanceType = (jitUnknown, jitNumberInteger,jitNumberInt64,jitNumberFloat,
|
||||
jitString, jitBoolean, jitNull, jitArray, jitObject);
|
||||
|
||||
when a Int64 value must be instantiated, the class identified with
|
||||
jitNumberInt64 is instantiated.
|
||||
|
||||
To customize the classes, the new class can be set using SetJSONInstanceType:
|
||||
|
||||
Procedure SetJSONInstanceType(AType : TJSONInstanceType; AClass : TJSONDataClass);
|
||||
Function GetJSONInstanceType(AType : TJSONInstanceType) : TJSONDataClass;
|
||||
|
||||
The function checks whether sane classes are specified.;
|
@ -45,6 +45,24 @@ Const
|
||||
AsJSONFormat = [foSingleLineArray,foSingleLineObject]; // These options make FormatJSON behave as AsJSON
|
||||
|
||||
Type
|
||||
TJSONData = Class;
|
||||
|
||||
{ TMJBaseObjectEnumerator }
|
||||
TJSONEnum = Record
|
||||
Key : TJSONStringType;
|
||||
KeyNum : Integer;
|
||||
Value : TJSONData;
|
||||
end;
|
||||
|
||||
TBaseJSONEnumerator = class
|
||||
public
|
||||
function GetCurrent: TJSONEnum; virtual; abstract;
|
||||
function MoveNext : Boolean; virtual; abstract;
|
||||
property Current: TJSONEnum read GetCurrent;
|
||||
end;
|
||||
|
||||
{ TMJObjectEnumerator }
|
||||
|
||||
|
||||
{ TJSONData }
|
||||
|
||||
@ -75,6 +93,8 @@ Type
|
||||
Constructor Create; virtual;
|
||||
Class function JSONType: TJSONType; virtual;
|
||||
Procedure Clear; virtual; Abstract;
|
||||
// Get enumerator
|
||||
function GetEnumerator: TBaseJSONEnumerator; virtual;
|
||||
Function FindPath(Const APath : TJSONStringType) : TJSONdata;
|
||||
Function GetPath(Const APath : TJSONStringType) : TJSONdata;
|
||||
Function Clone : TJSONData; virtual; abstract;
|
||||
@ -315,6 +335,7 @@ Type
|
||||
// Examine
|
||||
procedure Iterate(Iterator : TJSONArrayIterator; Data: TObject);
|
||||
function IndexOf(obj: TJSONData): Integer;
|
||||
function GetEnumerator: TBaseJSONEnumerator; override;
|
||||
// Manipulate
|
||||
Procedure Clear; override;
|
||||
function Add(Item : TJSONData): Integer;
|
||||
@ -408,6 +429,7 @@ Type
|
||||
destructor Destroy; override;
|
||||
class function JSONType: TJSONType; override;
|
||||
Function Clone : TJSONData; override;
|
||||
function GetEnumerator: TBaseJSONEnumerator; override;
|
||||
// Examine
|
||||
procedure Iterate(Iterator : TJSONObjectIterator; Data: TObject);
|
||||
function IndexOf(Item: TJSONData): Integer;
|
||||
@ -694,6 +716,102 @@ begin
|
||||
Result:=JPH;
|
||||
end;
|
||||
|
||||
Type
|
||||
{ TJSONEnumerator }
|
||||
|
||||
TJSONEnumerator = class(TBaseJSONEnumerator)
|
||||
Private
|
||||
FData : TJSONData;
|
||||
public
|
||||
Constructor Create(AData : TJSONData);
|
||||
function GetCurrent: TJSONEnum; override;
|
||||
function MoveNext : Boolean; override;
|
||||
end;
|
||||
|
||||
{ TJSONArrayEnumerator }
|
||||
|
||||
TJSONArrayEnumerator = class(TBaseJSONEnumerator)
|
||||
Private
|
||||
FData : TJSONArray;
|
||||
FCurrent : Integer;
|
||||
public
|
||||
Constructor Create(AData : TJSONArray);
|
||||
function GetCurrent: TJSONEnum; override;
|
||||
function MoveNext : Boolean; override;
|
||||
end;
|
||||
|
||||
{ TJSONObjectEnumerator }
|
||||
|
||||
TJSONObjectEnumerator = class(TBaseJSONEnumerator)
|
||||
Private
|
||||
FData : TJSONObject;
|
||||
FCurrent : Integer;
|
||||
public
|
||||
Constructor Create(AData : TJSONObject);
|
||||
function GetCurrent: TJSONEnum; override;
|
||||
function MoveNext : Boolean; override;
|
||||
end;
|
||||
|
||||
constructor TJSONObjectEnumerator.Create(AData: TJSONObject);
|
||||
begin
|
||||
FData:=AData;
|
||||
FCurrent:=-1;
|
||||
end;
|
||||
|
||||
function TJSONObjectEnumerator.GetCurrent: TJSONEnum;
|
||||
begin
|
||||
Result.KeyNum:=FCurrent;
|
||||
Result.Key:=FData.Names[FCurrent];
|
||||
Result.Value:=FData.Items[FCurrent];
|
||||
end;
|
||||
|
||||
function TJSONObjectEnumerator.MoveNext: Boolean;
|
||||
begin
|
||||
Inc(FCurrent);
|
||||
Result:=FCurrent<FData.Count;
|
||||
end;
|
||||
|
||||
{ TJSONArrayEnumerator }
|
||||
|
||||
constructor TJSONArrayEnumerator.Create(AData: TJSONArray);
|
||||
begin
|
||||
FData:=AData;
|
||||
FCurrent:=-1;
|
||||
end;
|
||||
|
||||
function TJSONArrayEnumerator.GetCurrent: TJSONEnum;
|
||||
begin
|
||||
Result.KeyNum:=FCurrent;
|
||||
Result.Key:=IntToStr(FCurrent);
|
||||
Result.Value:=FData.Items[FCurrent];
|
||||
end;
|
||||
|
||||
function TJSONArrayEnumerator.MoveNext: Boolean;
|
||||
begin
|
||||
Inc(FCurrent);
|
||||
Result:=FCurrent<FData.Count;
|
||||
end;
|
||||
|
||||
{ TJSONEnumerator }
|
||||
|
||||
constructor TJSONEnumerator.Create(AData: TJSONData);
|
||||
begin
|
||||
FData:=AData;
|
||||
end;
|
||||
|
||||
function TJSONEnumerator.GetCurrent: TJSONEnum;
|
||||
begin
|
||||
Result.Key:='';
|
||||
Result.KeyNum:=0;
|
||||
Result.Value:=FData;
|
||||
FData:=Nil;
|
||||
end;
|
||||
|
||||
function TJSONEnumerator.MoveNext: Boolean;
|
||||
begin
|
||||
Result:=FData<>Nil;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
{ TJSONData }
|
||||
@ -714,12 +832,12 @@ begin
|
||||
Clear;
|
||||
end;
|
||||
|
||||
Class procedure TJSONData.DoError(const Msg: String);
|
||||
class procedure TJSONData.DoError(const Msg: String);
|
||||
begin
|
||||
Raise EJSON.Create(Msg);
|
||||
end;
|
||||
|
||||
Class procedure TJSONData.DoError(const Fmt: String; Args: array of const);
|
||||
class procedure TJSONData.DoError(const Fmt: String; Args: array of const);
|
||||
begin
|
||||
Raise EJSON.CreateFmt(Fmt,Args);
|
||||
end;
|
||||
@ -746,6 +864,11 @@ begin
|
||||
JSONType:=jtUnknown;
|
||||
end;
|
||||
|
||||
function TJSONData.GetEnumerator: TBaseJSONEnumerator;
|
||||
begin
|
||||
Result:=TJSONEnumerator.Create(Self);
|
||||
end;
|
||||
|
||||
function TJSONData.FindPath(const APath: TJSONStringType): TJSONdata;
|
||||
|
||||
Var
|
||||
@ -1713,6 +1836,11 @@ begin
|
||||
Result:=FList.IndexOf(Obj);
|
||||
end;
|
||||
|
||||
function TJSONArray.GetEnumerator: TBaseJSONEnumerator;
|
||||
begin
|
||||
Result:=TJSONArrayEnumerator.Create(Self);
|
||||
end;
|
||||
|
||||
procedure TJSONArray.Clear;
|
||||
begin
|
||||
FList.Clear;
|
||||
@ -2169,6 +2297,11 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TJSONObject.GetEnumerator: TBaseJSONEnumerator;
|
||||
begin
|
||||
Result:=TJSONObjectEnumerator.Create(Self);
|
||||
end;
|
||||
|
||||
|
||||
function TJSONObject.DoFormatJSON(Options: TFormatOptions; CurrentIndent,
|
||||
Indent: Integer): TJSONStringType;
|
||||
|
@ -319,8 +319,124 @@ type
|
||||
Procedure ObjectCreateString;
|
||||
end;
|
||||
|
||||
{ TTestIterator }
|
||||
|
||||
TTestIterator = class(TTestJSON)
|
||||
private
|
||||
FData: TJSONData;
|
||||
Protected
|
||||
Procedure TearDown; override;
|
||||
Procedure TestSingle;
|
||||
Procedure TestLoop(ACount : Integer);
|
||||
Property Data : TJSONData Read FData Write FData;
|
||||
Published
|
||||
Procedure TestNull;
|
||||
Procedure TestInteger;
|
||||
Procedure TestInt64;
|
||||
Procedure TestFloat;
|
||||
Procedure TestBoolean;
|
||||
Procedure TestString;
|
||||
Procedure TestArray;
|
||||
Procedure TestObject;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
{ TTestIterator }
|
||||
|
||||
procedure TTestIterator.TearDown;
|
||||
begin
|
||||
FreeAndNil(FData);
|
||||
inherited TearDown;
|
||||
end;
|
||||
|
||||
procedure TTestIterator.TestSingle;
|
||||
|
||||
Var
|
||||
F : TJSONEnum;
|
||||
C : Integer;
|
||||
|
||||
begin
|
||||
C:=0;
|
||||
For F in Data do
|
||||
begin
|
||||
Inc(C);
|
||||
If C>1 then
|
||||
Fail(Data.ClassName+' loops more than once');
|
||||
AssertEquals(Data.ClassName+' has empty key','',F.Key);
|
||||
AssertEquals(Data.ClassName+' has empty numerical key',0,F.KeyNum);
|
||||
AssertSame(Data.ClassName+' returns data',Data,F.Value);
|
||||
end;
|
||||
If C<1 then
|
||||
Fail(Data.ClassName+' Loops not even once');
|
||||
end;
|
||||
|
||||
procedure TTestIterator.TestLoop(ACount: Integer);
|
||||
Var
|
||||
F : TJSONEnum;
|
||||
C : Integer;
|
||||
|
||||
begin
|
||||
C:=0;
|
||||
For F in Data do
|
||||
begin
|
||||
AssertEquals(Data.ClassName+' has correct string key',IntToStr(C),F.Key);
|
||||
AssertEquals(Data.ClassName+' has correct numerical key',C,F.KeyNum);
|
||||
AssertSame(Data.ClassName+' returns correct data',Data.Items[C],F.Value);
|
||||
Inc(C);
|
||||
end;
|
||||
AssertEquals(Data.ClassName+' correct loop count',ACount,C);
|
||||
end;
|
||||
|
||||
procedure TTestIterator.TestNull;
|
||||
begin
|
||||
Data:=TJSONNull.Create;
|
||||
TestSingle;
|
||||
end;
|
||||
|
||||
procedure TTestIterator.TestInteger;
|
||||
begin
|
||||
Data:=TJSONIntegerNumber.Create(1);
|
||||
TestSingle;
|
||||
end;
|
||||
|
||||
procedure TTestIterator.TestInt64;
|
||||
begin
|
||||
Data:=TJSONInt64Number.Create(1);
|
||||
TestSingle;
|
||||
end;
|
||||
|
||||
procedure TTestIterator.TestFloat;
|
||||
begin
|
||||
Data:=TJSONFloatNumber.Create(1.2);
|
||||
TestSingle;
|
||||
end;
|
||||
|
||||
procedure TTestIterator.TestBoolean;
|
||||
begin
|
||||
Data:=TJSONBoolean.Create(True);
|
||||
TestSingle;
|
||||
end;
|
||||
|
||||
procedure TTestIterator.TestString;
|
||||
begin
|
||||
Data:=TJSONString.Create('Data');
|
||||
TestSingle;
|
||||
end;
|
||||
|
||||
procedure TTestIterator.TestArray;
|
||||
begin
|
||||
Data:=TJSONArray.Create([1,2,3]);
|
||||
TestLoop(3);
|
||||
end;
|
||||
|
||||
procedure TTestIterator.TestObject;
|
||||
begin
|
||||
Data:=TJSONObject.Create(['0',1,'1',2,'2',3]);
|
||||
TestLoop(3);
|
||||
end;
|
||||
|
||||
{ TTestFactory }
|
||||
|
||||
procedure TTestFactory.DoSet;
|
||||
@ -3523,5 +3639,6 @@ initialization
|
||||
RegisterTest(TTestObject);
|
||||
RegisterTest(TTestJSONPath);
|
||||
RegisterTest(TTestFactory);
|
||||
RegisterTest(TTestIterator);
|
||||
end.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user