Explorar o código

* JSON Enumerator support

git-svn-id: trunk@25694 -
michael %!s(int64=11) %!d(string=hai) anos
pai
achega
cfaf45c7da

+ 63 - 1
packages/fcl-json/src/README.txt

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

+ 135 - 2
packages/fcl-json/src/fpjson.pp

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

+ 117 - 0
packages/fcl-json/tests/testjsondata.pp

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