* JSON Enumerator support

git-svn-id: trunk@25694 -
This commit is contained in:
michael 2013-10-06 14:52:27 +00:00
parent e977c09098
commit cfaf45c7da
3 changed files with 315 additions and 3 deletions

View File

@ -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.;

View File

@ -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;

View File

@ -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.