diff options
| author | michael <michael@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2011-01-05 22:49:00 +0000 |
|---|---|---|
| committer | michael <michael@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2011-01-05 22:49:00 +0000 |
| commit | 11b943ac7c21ebedff2987852f9e7deb5c826d6d (patch) | |
| tree | 1fe42fcf79904132e4f22f1912381e764e92fe5f /packages/fcl-json | |
| parent | 2611974405dd5619d66e9d33c3ecddd9af426858 (diff) | |
| download | fpc-11b943ac7c21ebedff2987852f9e7deb5c826d6d.tar.gz | |
* Support for RTTI streaming
git-svn-id: http://svn.freepascal.org/svn/fpc/trunk@16712 3ad0048d-3df7-0310-abae-a5850022a9f2
Diffstat (limited to 'packages/fcl-json')
| -rw-r--r-- | packages/fcl-json/src/fpjson.pp | 22 | ||||
| -rw-r--r-- | packages/fcl-json/src/fpjsonrtti.pp | 1014 | ||||
| -rw-r--r-- | packages/fcl-json/tests/testjson.lpi | 32 | ||||
| -rw-r--r-- | packages/fcl-json/tests/testjson.pp | 2 | ||||
| -rw-r--r-- | packages/fcl-json/tests/testjsondata.pp | 36 | ||||
| -rw-r--r-- | packages/fcl-json/tests/testjsonrtti.pp | 1770 |
6 files changed, 2867 insertions, 9 deletions
diff --git a/packages/fcl-json/src/fpjson.pp b/packages/fcl-json/src/fpjson.pp index 7951564853..591d991d95 100644 --- a/packages/fcl-json/src/fpjson.pp +++ b/packages/fcl-json/src/fpjson.pp @@ -382,7 +382,7 @@ Type // Examine procedure Iterate(Iterator : TJSONObjectIterator; Data: TObject); function IndexOf(Item: TJSONData): Integer; - Function IndexOfName(const AName: TJSONStringType): Integer; + Function IndexOfName(const AName: TJSONStringType; CaseInsensitive : Boolean = False): Integer; // Manipulate Procedure Clear; override; function Add(const AName: TJSONStringType; AValue: TJSONData): Integer; overload; @@ -416,11 +416,13 @@ Type Function StringToJSONString(const S : TJSONStringType) : TJSONStringType; Function JSONStringToString(const S : TJSONStringType) : TJSONStringType; - +Function JSONTypeName(JSONType : TJSONType) : String; implementation +Uses typinfo; + Resourcestring SErrCannotConvertFromNull = 'Cannot convert data from Null value'; SErrCannotConvertToNull = 'Cannot convert data to Null value'; @@ -521,6 +523,11 @@ begin Result:=Result+Copy(S,J,I-J+1); end; +function JSONTypeName(JSONType: TJSONType): String; +begin + Result:=GetEnumName(TypeInfo(TJSONType),Ord(JSONType)); +end; + { TJSONData } @@ -1316,6 +1323,8 @@ begin if not (foSingleLineArray in Options) then Result:=Result+sLineBreak end; + if not (foSingleLineArray in Options) then + Result:=Result+IndentString(Options, CurrentIndent); Result:=Result+']'; end; @@ -1870,9 +1879,16 @@ begin Result:=FHash.IndexOf(Item); end; -function TJSONObject.IndexOfName(const AName: TJSONStringType): Integer; +function TJSONObject.IndexOfName(const AName: TJSONStringType; CaseInsensitive : Boolean = False): Integer; + begin Result:=FHash.FindIndexOf(AName); + if (Result=-1) and CaseInsensitive then + begin + Result:=Count-1; + While (Result>=0) and (CompareText(Names[Result],AName)<>0) do + Dec(Result); + end; end; procedure TJSONObject.Clear; diff --git a/packages/fcl-json/src/fpjsonrtti.pp b/packages/fcl-json/src/fpjsonrtti.pp new file mode 100644 index 0000000000..15f0c46b8c --- /dev/null +++ b/packages/fcl-json/src/fpjsonrtti.pp @@ -0,0 +1,1014 @@ +unit fpjsonrtti; + +{$mode objfpc} + +interface + +uses + Classes, SysUtils, typinfo, fpjson, rttiutils, jsonparser; + +Type + + TJSONStreamEvent = Procedure (Sender : TObject; AObject : TObject; JSON : TJSONObject) of object; + TJSONPropertyEvent = Procedure (Sender : TObject; AObject : TObject; Info : PPropInfo; var Res : TJSONData) of object; + + TJSONStreamOption = (jsoStreamChildren, // If set, children will be streamed in 'Children' Property + jsoEnumeratedAsInteger, // Write enumerated as integer. Default is string. + jsoSetAsString, // Write Set as a string. Default is an array. + jsoSetEnumeratedAsInteger, // Write enumerateds in set array as integers. + jsoSetBrackets, // Use brackets when creating set as array + jsoComponentsInline, // Always stream components inline. Default is to stream name, unless csSubcomponent in ComponentStyle + jsoTStringsAsArray, // Stream TStrings as an array of strings. Associated objects are not streamed. + jsoTStringsAsObject, // Stream TStrings as an object : string = { object } + jsoDateTimeAsString, + jsoUseFormatString); // Use FormatString when creating JSON strings. + TJSONStreamOptions = Set of TJSONStreamOption; + + TJSONFiler = Class(TComponent) + Protected + Procedure Error(Const Msg : String); + Procedure Error(Const FMT : String; Args : Array of const); + end; + + { TJSONStreamer } + + TJSONStreamer = Class(TJSONFiler) + private + FAfterStreamObject: TJSONStreamEvent; + FBeforeStreamObject: TJSONStreamEvent; + FChildProperty: String; + FDateTimeFormat: String; + FOnStreamProperty: TJSONPropertyEvent; + FOptions: TJSONStreamOptions; + function GetChildProperty: String; + function IsChildStored: boolean; + function StreamChildren(AComp: TComponent): TJSONArray; + protected + function StreamClassProperty(Const AObject: TObject): TJSONData; virtual; + Function StreamProperty(Const AObject : TObject; Const PropertyName : String) : TJSONData; + Function StreamProperty(Const AObject : TObject; PropertyInfo : PPropInfo) : TJSONData; + Function FormatDateProp(const DateTime : TDateTime) : TJSONString; + Public + Constructor Create(AOwner : TComponent); override; + Destructor Destroy;override; + // + // Basic functions + // + // Use RTTI to stream object. + // If AObject is of type TStrings or TCollection, special treatment occurs: + // TStrings results in { Strings: [S,S,S] } or { Strings: { "S1" : O1, "S2" : O2 }} depending on Options. + // Collection results in { Items: [I,I,I] } + Function ObjectToJSON(Const AObject : TObject) : TJSONObject; + // Stream a collection - always returns an array + function StreamCollection(Const ACollection: TCollection): TJSONArray; + // Stream a TStrings instance as an array + function StreamTStringsArray(Const AStrings: TStrings): TJSONArray; + // Stream a TStrings instance as an object + function StreamTStringsObject(Const AStrings: TStrings): TJSONObject; + // Stream a TStrings instance. Takes into account Options. + function StreamTStrings(Const AStrings: TStrings): TJSONData; + // Stream a variant as JSON. + function StreamVariant(const Data: Variant): TJSONData; virtual; + // + // Some utility functions. + // + // Call ObjectToJSON and convert result to JSON String. + Function ObjectToJSONString(AObject : TObject) : TJSONStringType; + // Convert TSTrings to JSON string with array or Object. + Function StringsToJSON(Const Strings : TStrings; AsObject : Boolean = False) : TJSONStringType; + // Convert collection to JSON string + Function CollectionToJSON(Const ACollection : TCollection) : TJSONStringType; + // Convert variant to JSON String + Function VariantToJSON(Const Data : Variant) : TJSONStringType; + Published + // Format used when formatting DateTime values. Only used in conjunction with jsoDateTimeToString + Property DateTimeFormat : String Read FDateTimeFormat Write FDateTimeFormat; + // Options to use when streaming + Property Options : TJSONStreamOptions Read FOptions Write FOptions; + // Called before streaming an object with ObjectToJSON + Property BeforeStreamObject : TJSONStreamEvent Read FBeforeStreamObject Write FBeforeStreamObject; + // Called After streaming an object with ObjectToJSON + Property AfterStreamObject : TJSONStreamEvent Read FAfterStreamObject Write FAfterStreamObject; + // Called whenever a property was streamed. If Res is nil on return, no property is added. + Property OnStreamProperty : TJSONPropertyEvent Read FOnStreamProperty Write FOnStreamProperty; + // Property name to use when streaming child components. Default is "Children" + Property ChildProperty : String Read GetChildProperty Write FChildProperty Stored IsChildStored; + end; + + { TJSONDeStreamer } + TJSONRestorePropertyEvent = Procedure (Sender : TObject; AObject : TObject; Info : PPropInfo; AValue : TJSONData; Var Handled : Boolean) of object; + TJSONPropertyErrorEvent = Procedure (Sender : TObject; AObject : TObject; Info : PPropInfo; AValue : TJSONData; Error : Exception; Var Continue : Boolean) of object; + TJSONGetObjectEvent = Procedure (Sender : TOBject; AObject : TObject; Info : PPropInfo; AData : TJSONObject; DataName : TJSONStringType; Var AValue : TObject); + TJSONDeStreamer = Class(TJSONFiler) + private + FAfterReadObject: TJSONStreamEvent; + FBeforeReadObject: TJSONStreamEvent; + FOnGetObject: TJSONGetObjectEvent; + FOnPropError: TJSONpropertyErrorEvent; + FOnRestoreProp: TJSONRestorePropertyEvent; + procedure DeStreamClassProperty(AObject: TObject; PropInfo: PPropInfo; PropData: TJSONData); + protected + function GetObject(AInstance : TObject; const APropName: TJSONStringType; D: TJSONObject; PropInfo: PPropInfo): TObject; + procedure DoRestoreProperty(AObject: TObject; PropInfo: PPropInfo; PropData: TJSONData); virtual; + Function ObjectFromString(Const JSON : TJSONStringType) : TJSONData; virtual; + procedure RestoreProperty(AObject: TObject; PropInfo: PPropInfo; PropData: TJSONData); + Public + Constructor Create(AOwner : TComponent); override; + Destructor Destroy; override; + // Convert JSON object to properties of AObject + Procedure JSONToObject(Const JSON : TJSONStringType; AObject : TObject); + Procedure JSONToObject(Const JSON : TJSONObject; AObject : TObject); + // Convert JSON object/array to collection. + Procedure JSONToCollection(Const JSON : TJSONStringType; ACollection : TCollection); + Procedure JSONToCollection(Const JSON : TJSONData; ACollection : TCollection); + // Convert JSON array/object/string to TStrings + Procedure JSONToStrings(Const JSON : TJSONStringType; AStrings : TSTrings); + Procedure JSONToStrings(Const JSON : TJSONData; AStrings : TSTrings); + // Convert JSON data to a variant. Supports simple data types and arrays. + Function JSONToVariant(Data: TJSONData): Variant; + Function JSONToVariant(Data: TJSONStringType): Variant; + // Triggered at the start of each call to JSONToObject + Property BeforeReadObject : TJSONStreamEvent Read FBeforeReadObject Write FBeforeReadObject; + // Triggered at the end of each call to JSONToObject (not if exception happens) + Property AfterReadObject : TJSONStreamEvent Read FAfterReadObject Write FAfterReadObject; + // Called when a property will be restored. If 'Handled' is True on return, property is considered restored. + Property OnRestoreProperty : TJSONRestorePropertyEvent Read FOnRestoreProp Write FOnRestoreProp; + // Called when an error occurs when restoring a property. If Continue is False on return, exception is re-raised. + Property OnPropertyError : TJSONpropertyErrorEvent Read FOnPropError Write FOnPropError; + // Called when a object-typed property must be restored, and the property is Nil. Must return an instance for the property. + // Published Properties of the instance will be further restored with available data. + Property OngetObject : TJSONGetObjectEvent Read FOnGetObject Write FOnGetObject; + end; + + EJSONRTTI = Class(Exception); + + +implementation + +uses variants; + +ResourceString + SErrUnknownPropertyKind = 'Unknown property kind for property : "%s"'; + SErrUnsupportedPropertyKind = 'Unsupported property kind for property: "%s"'; + SErrUnsupportedVariantType = 'Unsupported variant type : %d'; + SErrUnsupportedArrayType = 'JSON array cannot be streamed to object of class "%s"'; + SErrUnsupportedJSONType = 'Cannot destream object from JSON data of type "%s"'; + SErrUnsupportedCollectionType = 'Unsupported JSON type for collections: "%s"'; + SErrUnsupportedCollectionItemType = 'Array element %d is not a valid type for a collection item: "%s"'; + SErrUnsupportedStringsItemType = 'Array element %d is not a valid type for a stringlist item: "%s"'; + SErrUnsupportedStringsType = 'Unsupported JSON type for stringlists: "%s"'; + SErrUnsupportedStringsObjectType = 'Object Element %s is not a valid type for a stringlist object: "%s"'; + SErrUnSupportedEnumDataType = 'Unsupported JSON type for enumerated property "%s" : "%s"'; + SErrUnsupportedVariantJSONType = 'Unsupported JSON type for variant value : "%s"'; + SErrUnsupportedObjectData = 'Unsupported JSON type for object property: "%s"'; + +{ TStreamChildrenHelper } + +Type + TSet = set of 0..31; // Used to (de)stream set properties. + + TStreamChildrenHelper = Class + Private + FChildren : TJSONArray; + FStreamer:TJSONStreamer; + procedure StreamChild(AChild: TComponent); + public + Function StreamChildren(AComponent : TComponent; AStreamer : TJSONStreamer): TJSONArray; + end; + + THackComponent = Class(TComponent); + +{ TJSONDeStreamer } + +function TJSONDeStreamer.ObjectFromString(const JSON: TJSONStringType): TJSONData; + +begin + With TJSONParser.Create(JSON) do + try + Result:=Parse; + finally + Free; + end; +end; + +constructor TJSONDeStreamer.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; + +destructor TJSONDeStreamer.Destroy; +begin + inherited Destroy; +end; + +procedure TJSONDeStreamer.JSONToObject(Const JSON: TJSONStringType; AObject: TObject); + +Var + D : TJSONData; + +begin + D:=ObjectFromString(JSON); + try + If D.JSONType=jtObject then + JSONToObject(D as TJSONObject,AObject) + else if D.JSONType=jtArray then + begin + If AObject is TStrings then + JSONToStrings(D,AObject as TSTrings) + else if AObject is TCollection then + JSONTOCollection(D,AObject as TCollection) + else + Error(SErrUnsupportedArrayType,[AObject.ClassName]) + end + else if (D.JSONType=jtString) and (AObject is TStrings) then + JSONToStrings(D,AObject as TStrings) + else + Error(SErrUnsupportedJSONType,[JSONTypeName(D.JSONType)]); + finally + FreeAndNil(D); + end; +end; + +Function TJSONDeStreamer.JSONToVariant(Data : TJSONData) : Variant; + +Var + I : integer; + +begin + Case Data.JSONType of + jtNumber : + Case TJSONNumber(Data).NumberType of + ntFloat : Result:=Data.AsFloat; + ntInteger : Result:=Data.AsInteger; + ntInt64 : Result:=Data.Asint64; + end; + jtString : + Result:=Data.AsString; + jtBoolean: + Result:=Data.AsBoolean; + jtNull: + Result:=Null; + jtArray : + begin + Result:=VarArrayCreate([0,Data.Count-1],varVariant); + For I:=0 to Data.Count-1 do + Result[i]:=JSONToVariant(Data.Items[i]); + end; + else + Error(SErrUnsupportedVariantJSONType,[GetEnumName(TypeInfo(TJSONType),Ord(Data.JSONType))]); + end; +end; + +function TJSONDeStreamer.JSONToVariant(Data: TJSONStringType): Variant; + +Var + D : TJSONData; + +begin + D:=ObjectFromString(Data); + try + Result:=JSONToVariant(D); + finally + D.Free; + end; +end; + +procedure TJSONDeStreamer.DeStreamClassProperty(AObject : TObject;PropInfo : PPropInfo; PropData : TJSONData); + +Var + O : TObject; + +begin + O:=GetObjectProp(AObject,PropInfo); + If O is TStrings then + JSONToStrings(PropData,O as TStrings) + else if (O is TCollection) then + JSONToCollection(PropData,O as TCollection) + else + begin + If (O=Nil) then + begin + If (PropData.JSONType=jtString) then + O:=GetObject(AObject,PropData.AsString,Nil,PropInfo) + else if (PropData.JSONType=jtObject) then + O:=GetObject(AObject,'',PropData as TJSONObject,PropInfo) + else + Error(SErrUnsupportedObjectData,[JsonTypeName(PropData.JSONType){GetEnumName(TypeInfo(TJSONType),Ord(PropData.JSONType))}]); + SetObjectProp(AObject,PropInfo,O); + end; + If (O<>Nil) and (PropData.JSONType=jtObject) then + JSONToObject(PropData as TJSONObject,O); + end; +end; + +procedure TJSONDeStreamer.RestoreProperty(AObject : TObject;PropInfo : PPropInfo; PropData : TJSONData); + +Var + B : Boolean; + +begin + try + B:=Not Assigned(FOnRestoreProp); + If Not B then + begin + FOnRestoreProp(Self,AObject,PropInfo,PropData,B); + If B then + exit; + end; + DoRestoreProperty(AObject,PropInfo,PropData); + except + On E : Exception do + If Assigned(FOnPropError) then + begin + B:=False; + FOnPropError(Self,AObject,PropInfo,PropData,E,B); + If Not B then + Raise; + end; + end; +end; + +procedure TJSONDeStreamer.DoRestoreProperty(AObject : TObject;PropInfo : PPropInfo; PropData : TJSONData); + +Var + PI : PPropInfo; + TI : PTypeInfo; + I,J,S : Integer; + D : Double; + A : TJSONArray; + JS : TJSONStringType; +begin + PI:=PropInfo; + TI:=PropInfo^.PropType; + case TI^.Kind of + tkUnknown : + Error(SErrUnknownPropertyKind,[PI^.Name]); + tkInteger : + SetOrdProp(AObject,PI,PropData.AsInteger); + tkInt64 : + SetOrdProp(AObject,PI,PropData.AsInt64); + tkEnumeration : + begin + if (PropData.JSONType=jtNumber) then + I:=PropData.AsInteger + else if PropData.JSONType=jtString then + I:=GetEnumValue(TI,PropData.AsString) + else + Error(SErrUnSupportedEnumDataType,[PI^.Name,GetEnumName(TypeInfo(TJSONType),Ord(PropData.JSONType))]); + SetOrdProp(AObject,PI,I); + end; + tkFloat : + begin + if (TI=TypeInfo(TDateTime)) and (PropData.JSONType=jtString) then + SetFloatProp(AObject,PI,StrToDateTime(PropData.AsString)) + else + SetFloatProp(AObject,PI,PropData.AsFloat) + end; + tkSet : + If PropData.JSONType=jtString then + SetSetProp(AObject,PI,PropData.AsString) + else if (PropData.JSONType=jtArray) then + begin + A:=PropData as TJSONArray; + TI:=GetTypeData(TI)^.CompType; + S:=0; + For I:=0 to A.Count-1 do + begin + if A.types[i]=jtNumber then + J:=A.Integers[i] + else + J:=GetEnumValue(TI,A.strings[i]); + TSet(S):=TSet(S)+[j]; + end; + SetOrdProp(AObject,PI,S); + end; + tkChar: + begin + JS:=PropData.AsString; + If (JS<>'') then + SetOrdProp(AObject,PI,Ord(JS[1])); + end; + tkSString, + tkLString, + tkAString: + SetStrProp(AObject,PI,PropData.AsString); + tkWString : + SetWideStrProp(AObject,PI,PropData.AsString); + tkVariant: + SetVariantProp(AObject,PI,JSONToVariant(PropData)); + tkClass: + DeStreamClassProperty(AObject,PI,PropData); + tkWChar : + begin + JS:=PropData.asString; + If (JS<>'') then + SetOrdProp(AObject,PI,Ord(JS[1])); + end; + tkBool : + SetOrdProp(AObject,PI,Ord(PropData.AsBoolean)); + tkQWord : + SetOrdProp(AObject,PI,Trunc(PropData.AsFloat)); + tkObject, + tkArray, + tkRecord, + tkInterface, + tkDynArray, + tkInterfaceRaw, + tkProcVar, + tkMethod : + Error(SErrUnsupportedPropertyKind,[PI^.Name]); + tkUString : + SetUnicodeStrProp(AObject,PI,PropData.AsString); + tkUChar: + begin + JS:=PropData.asString; + If (JS<>'') then + SetOrdProp(AObject,PI,Ord(JS[1])); + end; + end; +end; + +procedure TJSONDeStreamer.JSONToObject(Const JSON: TJSONObject; AObject: TObject); +Var + I,J : Integer; + PIL : TPropInfoList; + +begin + If Assigned(FBeforeReadObject) then + FBeforeReadObject(Self,AObject,JSON); + If (AObject is TStrings) then + JSONToStrings(JSON,AObject as TStrings) + else If (AObject is TCollection) then + JSONToCollection(JSON, AObject as TCollection) + else + begin + Pil:=TPropInfoList.Create(AObject,tkProperties); + try + For I:=0 to PIL.Count-1 do + begin + J:=JSON.IndexOfName(Pil.Items[i]^.Name); + If (J<>-1) then + RestoreProperty(AObject,PIL.Items[i],JSON.Items[J]); + end; + finally + FreeAndNil(PIL); + end; + end; + If Assigned(FAfterReadObject) then + FAfterReadObject(Self,AObject,JSON) +end; + +procedure TJSONDeStreamer.JSONToCollection(const JSON: TJSONStringType; + ACollection: TCollection); +Var + D : TJSONData; + +begin + D:=ObjectFromString(JSON); + try + JSONToCollection(D,ACollection); + finally + D.Free; + end; +end; + +procedure TJSONDeStreamer.JSONToCollection(const JSON: TJSONData; + ACollection: TCollection); + +Var + I : integer; + A : TJSONArray; + O : TJSONObject; + +begin + If (JSON.JSONType=jtArray) then + A:=JSON As TJSONArray + else if JSON.JSONType=jtObject then + A:=(JSON as TJSONObject).Arrays['Items'] + else + Error(SErrUnsupportedCollectionType,[JSONTypeName(JSON.JSONType)]); + ACollection.Clear; + For I:=0 to A.Count-1 do + If (A.Types[i]<>jtObject) then + Error(SErrUnsupportedCollectionItemType,[I,JSONTypeName(A.Types[I])]) + else + JSONToObject(A.Objects[i],ACollection.Add); +end; + +procedure TJSONDeStreamer.JSONToStrings(const JSON: TJSONStringType; + AStrings: TSTrings); +Var + D : TJSONData; + +begin + D:=ObjectFromString(JSON); + try + JSONToStrings(D,AStrings); + finally + D.Free; + end; +end; + +Function TJSONDeStreamer.GetObject(AInstance : TObject; Const APropName : TJSONStringType; D : TJSONObject; PropInfo : PPropInfo) : TObject; + +Var + C : TClass; + +begin + Result:=Nil; + If Assigned(FOnGetObject) then + FOnGetObject(Self,AInstance,PropInfo,D,APropName,Result); + If (Result=Nil) and (AInstance is TComponent) and Assigned(PropInfo) then + begin + C:=GetTypeData(Propinfo^.PropType)^.ClassType; + If C.InheritsFrom(TComponent) then + Result:=TComponentClass(C).Create(TComponent(AInstance)); + end; +end; + +procedure TJSONDeStreamer.JSONToStrings(const JSON: TJSONData; + AStrings: TSTrings); + +Var + O : TJSONObject; + D : TJSONData; + I : Integer; + IO : TObject; + N : TJSONStringType; + +begin + Case JSON.JSONType of + jtString: + AStrings.Text:=JSON.AsString; + jtArray: + begin + AStrings.Clear; + For I:=0 to JSON.Count-1 do + begin + if not (JSON.Items[i].JSONType=jtString) then + Error(SErrUnsupportedStringsItemType,[i,JSONTypeName(JSON.Items[i].JSONType)]); + AStrings.Add(JSON.Items[i].AsString); + end; + end; + jtObject: + begin + O:=JSON As TJSONObject; + If (O.Count=1) and (O.Names[0]='Strings') and (O.Items[0].JSONType=jtArray) then + JSONToStrings(O.Items[0],AStrings) + else + begin + AStrings.Clear; + For I:=0 to O.Count-1 do + begin + D:=O.Items[i]; + N:=O.Names[i]; + If D.JSONType=jtNull then + IO:=Nil + else if D.JSONType=jtObject then + IO:=GetObject(AStrings,N,TJSONOBject(D),Nil) + else + Error(SErrUnsupportedStringsObjectType,[D,JSONTypeName(D.JSONType)]); + AStrings.AddObject(O.Names[i],IO); + end; + end; + end; + else + Error(SErrUnsupportedStringsType,[JSONTypeName(JSON.JSONType)]); + end; +end; + +Procedure TStreamChildrenHelper.StreamChild(AChild : TComponent); + +begin + FChildren.Add(FStreamer.ObjectToJSON(AChild)); +end; + +Function TStreamChildrenHelper.StreamChildren(AComponent : TComponent; AStreamer : TJSONStreamer): TJSONArray; + +begin + FStreamer:=AStreamer; + Result:=TJSONArray.Create; + try + FChildren:=Result; + THackComponent(AComponent).GetChildren(@StreamChild,AComponent); + except + FreeAndNil(Result); + Raise; + end; +end; + +{ TJSONFiler } + +procedure TJSONFiler.Error(Const Msg: String); +begin + Raise EJSONRTTI.Create(Name+' : '+Msg); +end; + +procedure TJSONFiler.Error(Const FMT: String; Args: array of const); +begin + Raise EJSONRTTI.CreateFmt(Name+' : '+FMT,Args); +end; + +{ TJSONStreamer } + +constructor TJSONStreamer.Create(AOwner: TComponent); +begin + Inherited; +end; + +destructor TJSONStreamer.Destroy; +begin + Inherited; +end; + + +Function TJSONStreamer.StreamChildren(AComp : TComponent) : TJSONArray; + +begin + With TStreamChildrenHelper.Create do + try + Result:=StreamChildren(AComp,Self); + finally + Free; + end; +end; + +function TJSONStreamer.GetChildProperty: String; +begin + Result:=FChildProperty; + If (Result='') then + Result:='Children'; +end; + +function TJSONStreamer.IsChildStored: boolean; +begin + Result:=(GetChildProperty<>'Children'); +end; + +function TJSONStreamer.ObjectToJSON(Const AObject: TObject): TJSONObject; + +Var + PIL : TPropInfoList; + PD : TJSONData; + I : Integer; + +begin + Result:=Nil; + If (AObject=Nil) then + Exit; + Result:=TJSONObject.Create; + try + If Assigned(FBeforeStreamObject) then + FBeforeStreamObject(Self,AObject,Result); + If AObject is TStrings then + Result.Add('Strings',StreamTStrings(Tstrings(AObject))) + else If AObject is TCollection then + Result.Add('Items',StreamCollection(TCollection(AObject))) + else + begin + PIL:=TPropInfoList.Create(AObject,tkProperties); + try + For I:=0 to PIL.Count-1 do + begin + PD:=StreamProperty(AObject,PIL.Items[i]); + If (PD<>Nil) then + Result.Add(PIL.Items[I]^.Name,PD); + end; + finally + FReeAndNil(Pil); + end; + If (jsoStreamChildren in Options) and (AObject is TComponent) then + Result.Add(ChildProperty,StreamChildren(TComponent(AObject))); + If Assigned(FAfterStreamObject) then + FAfterStreamObject(Self,AObject,Result); + end; + except + FreeAndNil(Result); + Raise; + end; +end; + +function TJSONStreamer.StreamProperty(Const AObject: TObject; Const PropertyName : String): TJSONData; + +begin + Result:=StreamProperty(AObject,GetPropInfo(AObject,PropertyName)); +end; + +Function TJSONStreamer.StreamVariant(Const Data : Variant): TJSONData; + +Var + A : TJSONArray; + I : Integer; + +begin + Result:=Nil; + If VarIsArray(Data) then + begin + A:=TJSONArray.Create; + try + For I:=VarArrayLowBound(Data,1) to VarArrayHighBound(Data,1) do + A.Add(StreamVariant(Data[i])); + except + FreeAndNil(A); + Raise; + end; + Exit(A); + end; + If VarIsEmpty(Data) or VarisNull(Data) or (Data=UnAssigned) then + Exit(TJSONNull.Create); + Case VarType(Data) of + varshortint, + varbyte, + varword, + varsmallint, + varinteger : + Result:=TJSONIntegerNumber.Create(Data); + varlongword, + varint64 : + Result:=TJSONInt64Number.Create(Data); + vardecimal, + varqword, + varsingle, + vardouble, + varCurrency : + Result:=TJSONFloatNumber.Create(Data); + varString, + varolestr : + Result:=TJSONString.Create(Data); + varboolean : + Result:=TJSONBoolean.Create(Data); + varDate : + if jsoDateTimeAsString in Options then + Result:=FormatDateProp(Data) + else + Result:=TJSONFloatNumber.Create(Data); + else + Error(SErrUnsupportedVariantType,[VarType(Data)]) + end; +end; + +function TJSONStreamer.ObjectToJSONString(AObject: TObject): TJSONStringType; + +Var + O : TJSONData; + +begin + O:=ObjectToJSON(AObject); + try + if (jsoUseFormatString in Options) then + Result:=O.FormatJSON() + else + Result:=O.AsJSON; + finally + FreeAndNil(O); + end; +end; + +function TJSONStreamer.StringsToJSON(Const Strings: TStrings; AsObject: Boolean = False): TJSONStringType; + +Var + D : TJSONData; + +begin + If ASObject then + D:=StreamTSTringsObject(Strings) + else + D:=StreamTStringsArray(Strings); + try + if (jsoUseFormatString in Options) then + Result:=D.FormatJSON + else + Result:=D.AsJSON; + finally + FreeAndNil(D); + end; +end; + +function TJSONStreamer.CollectionToJSON(const ACollection: TCollection + ): TJSONStringType; + +Var + D : TJSONArray; + +begin + D:=StreamCollection(ACollection); + try + if (jsoUseFormatString in Options) then + Result:=D.FormatJSON() + else + Result:=D.AsJSON; + finally + FreeAndNil(D); + end; +end; + +function TJSONStreamer.VariantToJSON(const Data: Variant): TJSONStringType; + +Var + D : TJSONData; + +begin + D:=StreamVariant(Data); + try + if (jsoUseFormatString in Options) then + Result:=D.FormatJSON() + else + Result:=D.AsJSON; + finally + FreeAndNil(D); + end; +end; + +Function TJSONStreamer.StreamTStringsArray(Const AStrings : TStrings) : TJSONArray; + +Var + I : Integer; + +begin + Result:=TJSONArray.Create; + try + For I:=0 to AStrings.Count-1 do + Result.Add(AStrings[i]); + except + FreeAndNil(Result); + Raise; + end; +end; + +function TJSONStreamer.StreamTStringsObject(Const AStrings: TStrings): TJSONObject; + +Var + I : Integer; + O : TJSONData; + +begin + Result:=TJSONObject.Create; + try + For I:=0 to AStrings.Count-1 do + begin + O:=ObjectToJSON(AStrings.Objects[i]); + If O=Nil then + O:=TJSONNull.Create; + Result.Add(AStrings[i],O); + end; + except + FreeAndNil(Result); + Raise; + end; +end; + +function TJSONStreamer.StreamTStrings(Const AStrings: TStrings): TJSONData; +begin + If jsoTStringsAsArray in Options then + Result:=StreamTStringsArray(AStrings) + else If jsoTStringsAsObject in Options then + Result:=StreamTStringsObject(AStrings) + else + Result:=TJSONString.Create(AStrings.Text); +end; + + +Function TJSONStreamer.StreamCollection(Const ACollection : TCollection) : TJSONArray; + +Var + I : Integer; + +begin + Result:=TJSONArray.Create; + try + For I:=0 to ACollection.Count-1 do + Result.Add(ObjectToJSON(ACollection.Items[i])); + except + FreeAndNil(Result); + Raise; + end; +end; + +Function TJSONStreamer.StreamClassProperty(Const AObject : TObject): TJSONData; + +Var + C : TCollection; + I : integer; + +begin + Result:=Nil; + If (AObject=Nil) then + Result:=TJSONNull.Create() + else if (AObject is TComponent) then + begin + if (csSubComponent in TComponent(AObject).ComponentStyle) or (jsoComponentsInline in Options) then + Result:=ObjectToJSON(AObject) + else + Result:=TJSONString.Create(TComponent(AObject).Name); + end + else if (AObject is TStrings) then + Result:=StreamTStrings(TStrings(AObject)) + else if (AObject is TCollection) then + Result:=StreamCollection(TCollection(Aobject)) + else // Normally, this is only TPersistent. + Result:=ObjectToJSON(AObject); +end; + +function TJSONStreamer.StreamProperty(Const AObject: TObject; PropertyInfo: PPropInfo): TJSONData; + +Var + PI : PPropInfo; + PT : PTypeInfo; + S,I : integer; + +begin + Result:=Nil; + PI:=PropertyInfo; + PT:=PI^.PropType; + Case PT^.Kind of + tkUnknown : + Error(SErrUnknownPropertyKind,[PI^.Name]); + tkInteger : + Result:=TJSONIntegerNumber.Create(GetOrdProp(AObject,PI)); + tkEnumeration : + if jsoEnumeratedAsInteger in Options then + Result:=TJSONIntegerNumber.Create(GetOrdProp(AObject,PI)) + else + Result:=TJSONString.Create(GetEnumName(PT,GetOrdProp(AObject,PI))); + tkFloat : + if (PT=TypeInfo(TDateTime)) and (jsoDateTimeAsString in Options) then + Result:=FormatDateProp(GetFloatProp(AObject,PI)) + else + Result:=TJSONFloatNumber.Create(GetFloatProp(AObject,PI)); + tkSet : + If jsoSetAsString in Options then + Result:=TJSONString.Create(GetSetProp(AObject,PI,jsoSetBrackets in Options)) + else + begin + PT:=GetTypeData(PT)^.CompType; + S:=GetOrdProp(AObject,PI); + Result:=TJSONArray.Create; + try + for i:=0 to 31 do + if (i in TSet(S)) then + if jsoSetEnumeratedAsInteger in Options then + TJSONArray(Result).Add(i) + else + TJSONArray(Result).Add(GetEnumName(PT, i)); + except + FreeAndNil(Result); + Raise; + end; + end; + tkChar: + Result:=TJSONString.Create(Char(GetOrdProp(AObject,PI))); + tkSString, + tkLString, + tkAString: + Result:=TJSONString.Create(GetStrProp(AObject,PI)); + tkWString : + Result:=TJSONString.Create(GetWideStrProp(AObject,PI)); + tkVariant: + Result:=StreamVariant(GetVariantProp(AObject,PI)); + tkClass: + Result:=StreamClassProperty(GetObjectProp(AObject,PI)); + tkWChar : + Result:=TJSONString.Create(WideChar(GetOrdProp(AObject,PI))); + tkBool : + Result:=TJSONBoolean.Create(GetOrdProp(AObject,PropertyInfo)<>0); + tkInt64 : + Result:=TJSONInt64Number.Create(GetOrdProp(AObject,PropertyInfo)); + tkQWord : + Result:=TJSONFloatNumber.Create(GetOrdProp(AObject,PropertyInfo)); + tkObject, + tkArray, + tkRecord, + tkInterface, + tkDynArray, + tkInterfaceRaw, + tkProcVar, + tkMethod : + Error(SErrUnsupportedPropertyKind,[PI^.Name]); + tkUString : + Result:=TJSONString.Create(GetWideStrProp(AObject,PI)); + tkUChar: + Result:=TJSONString.Create(UnicodeChar(GetOrdProp(AObject,PI))); + end; + If Assigned(FOnStreamProperty) then + FOnStreamProperty(Self,AObject,PI,Result); +end; + +function TJSONStreamer.FormatDateProp(Const DateTime: TDateTime): TJSONString; + +Var + S: String; + +begin + if (DateTimeFormat<>'') then + S:=FormatDateTime(DateTimeFormat,DateTime) + else if Frac(DateTime)=0 then + S:=DateToStr(DateTime) + else if Trunc(DateTime)=0 then + S:=TimeToStr(DateTime) + else + S:=DateTimeToStr(DateTime); + Result:=TJSONString.Create(S); +end; + +end. + diff --git a/packages/fcl-json/tests/testjson.lpi b/packages/fcl-json/tests/testjson.lpi index 601c243740..9d9fb168ca 100644 --- a/packages/fcl-json/tests/testjson.lpi +++ b/packages/fcl-json/tests/testjson.lpi @@ -1,18 +1,20 @@ <?xml version="1.0"?> <CONFIG> <ProjectOptions> - <Version Value="8"/> + <Version Value="9"/> <General> <Flags> <LRSInOutputDirectory Value="False"/> </Flags> <SessionStorage Value="InProjectDir"/> <MainUnit Value="0"/> - <TargetFileExt Value=""/> </General> <VersionInfo> - <StringTable Comments="" CompanyName="" FileDescription="" FileVersion="0.0.0.0" InternalName="" LegalCopyright="" LegalTrademarks="" OriginalFilename="" ProductName="" ProductVersion=""/> + <StringTable ProductVersion=""/> </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="default" Default="True"/> + </BuildModes> <PublishOptions> <Version Value="2"/> <IgnoreBinaries Value="False"/> @@ -22,6 +24,7 @@ <RunParams> <local> <FormatVersion Value="1"/> + <CommandLineParams Value="--format=plain --suite=TCJSONStreamer"/> <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/> </local> </RunParams> @@ -39,7 +42,7 @@ <PackageName Value="FCL"/> </Item4> </RequiredPackages> - <Units Count="3"> + <Units Count="5"> <Unit0> <Filename Value="testjson.pp"/> <IsPartOfProject Value="True"/> @@ -55,19 +58,38 @@ <IsPartOfProject Value="True"/> <UnitName Value="testjsondata"/> </Unit2> + <Unit3> + <Filename Value="testjsonrtti.pp"/> + <IsPartOfProject Value="True"/> + <UnitName Value="testjsonrtti"/> + </Unit3> + <Unit4> + <Filename Value="../src/fpjsonrtti.pp"/> + <IsPartOfProject Value="True"/> + <UnitName Value="fpjsonrtti"/> + </Unit4> </Units> </ProjectOptions> <CompilerOptions> <Version Value="9"/> <SearchPaths> - <OtherUnitFiles Value="../src/"/> + <OtherUnitFiles Value="../src"/> </SearchPaths> <Parsing> <SyntaxOptions> <UseAnsiStrings Value="False"/> </SyntaxOptions> </Parsing> + <Linking> + <Debugging> + <GenerateDebugInfo Value="True"/> + <UseHeaptrc Value="True"/> + </Debugging> + </Linking> <Other> + <CompilerMessages> + <UseMsgFile Value="True"/> + </CompilerMessages> <CompilerPath Value="$(CompPath)"/> </Other> </CompilerOptions> diff --git a/packages/fcl-json/tests/testjson.pp b/packages/fcl-json/tests/testjson.pp index 67476fda04..e2e430b327 100644 --- a/packages/fcl-json/tests/testjson.pp +++ b/packages/fcl-json/tests/testjson.pp @@ -18,7 +18,7 @@ program testjson; uses Classes, consoletestrunner, testjsondata, testjsonparser, - fpcunitconsolerunner; + fpcunitconsolerunner, testjsonrtti, fpjsonrtti; type { TLazTestRunner } TMyTestRunner = class(TTestRunner) diff --git a/packages/fcl-json/tests/testjsondata.pp b/packages/fcl-json/tests/testjsondata.pp index 32b89c0877..644509d667 100644 --- a/packages/fcl-json/tests/testjsondata.pp +++ b/packages/fcl-json/tests/testjsondata.pp @@ -194,6 +194,7 @@ type procedure TestExtract; Procedure TestNonExistingAccessError; Procedure TestFormat; + Procedure TestFind; end; @@ -1968,6 +1969,41 @@ begin end; end; +procedure TTestObject.TestFind; + +Const + A = 'A'; + S = 'A string'; + B = 'a'; + S2 = 'Another string'; + C = 'c'; + S3 = 'Yet Another string'; + +Var + J : TJSONObject; + +begin + J:=TJSONObject.Create([A,S,B,S2,C,S3]); + try + TestJSONType(J,jtObject); + TestIsNull(J,False); + TestItemCount(J,3); + TestJSONType(J[A],jtString); + TestJSONType(J[B],jtString); + TestJSON(J,'{ "A" : "'+S+'", "a" : "'+S2+'", "c" : "'+S3+'" }'); + AssertEquals('Nonexisting, case sensitive',-1,J.IndexOfName('D')); + AssertEquals('Nonexisting, case insensitive',-1,J.IndexOfName('D',True)); + AssertEquals('1 Existing , case sensitive',0,J.IndexOfName(A)); + AssertEquals('2 Existing exact match, case insensitive',0,J.IndexOfName(A,true)); + AssertEquals('3 Existing , case sensitive',1,J.IndexOfName(B)); + AssertEquals('4 Existing exact match, case insensitive',1,J.IndexOfName(B,true)); + AssertEquals('5 Existing , case sensitive again',2,J.IndexOfName(C)); + AssertEquals('6 Existing case-insensitive match, case insensitive',2,J.IndexOfName(Uppercase(C),true)); + finally + FreeAndNil(J); + end; +end; + procedure TTestObject.TestCreateString; diff --git a/packages/fcl-json/tests/testjsonrtti.pp b/packages/fcl-json/tests/testjsonrtti.pp new file mode 100644 index 0000000000..01ffa7da76 --- /dev/null +++ b/packages/fcl-json/tests/testjsonrtti.pp @@ -0,0 +1,1770 @@ +unit testjsonrtti; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, fpcunit, testutils, testregistry, typinfo, fpjson, + testcomps, testjsondata, fpjsonrtti; + +type + + { TCJSONStreamer } + + TCJSONStreamer = class(TTestJSON) + private + FRJ : TJSONStreamer; + FSR : TJSONObject; + FToFree : TObject; + FCalled : Boolean; + procedure DoStreamProperty1(Sender: TObject; AObject: TObject; Info: PPropInfo; var Res: TJSONData); + protected + procedure SetUp; override; + procedure TearDown; override; + Procedure AssertEquals(AMessage : String; Expected,Actual : TJSONType); overload; + Procedure AssertPropCount(ACount : Integer); + Function AssertProperty(APropName : String; AType : TJSONType) : TJSONData; + Procedure AssertProp(APropName : String; AValue : Boolean); + Procedure AssertProp(APropName : String; AValue : Integer); + procedure AssertProp(APropName : String; AValue: String); + procedure AssertProp(APropName : String; AValue: TJSONFloat); + procedure AssertProp(APropName : String; AValue : Array of String); + procedure AssertProp(APropName : String; AValue : Array of Integer); + function CreateVariantComp : TVariantComponent; + procedure AssertNullProp(APropName : String); + Function AssertObjectProp(APropName : String) : TJSONObject; + Function AssertArrayProp(APropName : String) : TJSONArray; + Function StreamObject(AObject : TObject) : TJSONObject; + Property RJ : TJSONStreamer read FRJ; + Property SR : TJSONObject Read FSR Write FSR; + published + procedure TestNil; + procedure TestEmpty; + procedure TestEmptyComponent; + procedure TestWriteBoolean; + procedure TestWriteInteger; + procedure TestWriteString; + procedure TestWriteFloat; + procedure TestWriteFloat2; + procedure TestWriteFloat3; + procedure TestWriteFloat4; + procedure TestWriteFloat5; + procedure TestEnum1; + procedure TestEnum2; + Procedure TestSet1; + Procedure TestSet2; + Procedure TestSet3; + Procedure TestSet4; + Procedure TestObjectNil; + Procedure TestComponentProp1; + Procedure TestComponentProp2; + Procedure TestCollectionProp1; + Procedure TestCollectionProp2; + Procedure TestPersistentProp1; + Procedure TestStringsProp1; + Procedure TestStringsProp2; + procedure TestStringsProp3; + procedure TestStringsProp4; + procedure TestStringsArray; + procedure TestStringsObject; + procedure TestStringsStream1; + procedure TestStringsStream2; + procedure TestStringsStream3; + procedure TestStringsStream4; + procedure TestStringsStream5; + procedure TestCollectionStream; + procedure TestCollectionStream2; + procedure TestOnStreamProperty; + Procedure TestDateTimeProp; + Procedure TestDateTimeProp2; + Procedure TestDateTimeProp3; + procedure TestDateTimeProp4; + procedure TestDateTimeProp5; + procedure TestDateTimeProp6; + procedure TestDateTimeProp7; + Procedure TestVariantShortint; + Procedure TestVariantbyte; + Procedure TestVariantword; + Procedure TestVariantsmallint; + Procedure TestVariantinteger; + Procedure TestVariantlongword; + Procedure TestVariantint64; + Procedure TestVariantqword; + Procedure TestVariantsingle; + Procedure TestVariantdouble; + Procedure TestVariantCurrency; + Procedure TestVariantString; + Procedure TestVariantolestr; + Procedure TestVariantboolean; + Procedure TestVariantDate; + procedure TestVariantDate2; + Procedure TestVariantArray; + Procedure TestMultipleProps; + Procedure TestObjectToJSONString; + Procedure TestStringsToJSONString; + Procedure TestCollectionToJSONString; + Procedure TestChildren; + Procedure TestChildren2; + end; + + { TCJSONDeStreamer } + + TCJSONDeStreamer = class(TTestJSON) + private + FDS : TJSONDeStreamer; + FJD : TJSONData; + FToFree : TObject; + FCalled : Boolean; + procedure DeStream(JSON: TJSONStringType; AObject: TObject); + procedure DeStream(JSON: TJSONObject; AObject: TObject); + protected + procedure SetUp; override; + procedure TearDown; override; + Procedure AssertVarType(Msg : String; AVarType : TVarType; Const Variant : Variant); + Property DS : TJSONDeStreamer Read FDS; + Property JD : TJSONData Read FJD Write FJD; + Property Tofree : TObject Read FToFree Write FToFree; + published + Procedure TestVariantInteger; + Procedure TestVariantFloat; + Procedure TestVariantInt64; + Procedure TestVariantBoolean; + Procedure TestVariantNull; + Procedure TestVariantString; + Procedure TestVariantArray; + procedure TestEmpty; + procedure TestBoolean; + procedure TestInteger; + procedure TestString; + procedure TestFloat; + procedure TestFloat2; + procedure TestFloat3; + procedure TestFloat4; + procedure TestFloat5; + procedure TestEnum1; + procedure TestEnum2; + Procedure TestSet1; + Procedure TestSet2; + Procedure TestSet3; + Procedure TestSet4; + Procedure TestVariantProp; + procedure TestCollection; + procedure TestCollection2; + procedure TestCollection3; + procedure TestCollection4; + procedure TestCollection5; + procedure TestCollection6; + procedure TestCollectionProp; + procedure TestCollectionProp2; + procedure TestStrings; + procedure TestStrings2; + procedure TestStrings3; + end; + +implementation + +uses variants; + +{ TCJSONDeStreamer } + +procedure TCJSONDeStreamer.SetUp; +begin + inherited SetUp; + FDS:=TJSONDeStreamer.Create(Nil) +end; + +procedure TCJSONDeStreamer.TearDown; +begin + FreeAndNil(FDS); + FreeAndNil(FJD); + FreeAndNil(FToFree); + inherited TearDown; +end; + +procedure TCJSONDeStreamer.AssertVarType(Msg: String; AVarType: TVarType; + const Variant: Variant); +begin + AssertEquals(Msg,VarTypeAsText(AVarType),VarTypeAsText(VarType(Variant))); +end; + +procedure TCJSONDeStreamer.TestVariantInteger; + +Var + V : Variant; + +begin + JD:=TJSOnIntegerNumber.Create(12); + V:=DS.JSONToVariant(JD); + AssertVarType('Integer data',varInteger,V); + AssertEquals('Integer value',12,V); +end; + +procedure TCJSONDeStreamer.TestVariantFloat; +Var + V : Variant; + +begin + JD:=TJSOnFloatNumber.Create(1.2); + V:=DS.JSONToVariant(JD); + AssertVarType('Double data',varDouble,V); + AssertEquals('Float value',1.2,V); +end; + +procedure TCJSONDeStreamer.TestVariantInt64; +Var + V : Variant; + +begin + JD:=TJSONInt64Number.Create(123); + V:=DS.JSONToVariant(JD); + AssertVarType('Int64 data',varInt64,V); + AssertEquals('Int64 value',123,V); +end; + +procedure TCJSONDeStreamer.TestVariantBoolean; +Var + V : Variant; + +begin + JD:=TJSONBoolean.Create(True); + V:=DS.JSONToVariant(JD); + AssertVarType('Boolean data',varBoolean,V); + AssertEquals('Boolean value',True,V); +end; + +procedure TCJSONDeStreamer.TestVariantNull; +Var + V : Variant; + +begin + JD:=TJSONNull.Create(); + V:=DS.JSONToVariant(JD); + AssertVarType('Null data',varNull,V); +end; + +procedure TCJSONDeStreamer.TestVariantString; +Var + V : Variant; + +begin + JD:=TJSONString.Create('A string'); + V:=DS.JSONToVariant(JD); + AssertVarType('String data',varString,V); + AssertEquals('String data','A string',V); +end; + +procedure TCJSONDeStreamer.TestVariantArray; +Var + V : Variant; +begin + JD:=TJSONArray.Create([1,2,3]); + V:=DS.JSONToVariant(JD); + AssertEQuals('Variant is array',true,VarIsArray(V)); + AssertEquals('Lower bound is zero ',0,VarArrayLowBound(V,1)); + AssertEquals('Higher bound is count-1 ',2,VarArrayHighBound(V,1)); + AssertEquals('Element 0 value correct ',1,V[0]); + AssertEquals('Element 1 value correct ',2,V[1]); + AssertEquals('Element 2 value correct ',3,V[2]); +end; + +procedure TCJSONDeStreamer.TestEmpty; +begin + FTofree:=TComponent.Create(Nil); + DS.JSONToObject('{}',FTofree); + AssertEquals('Empty name','',TComponent(FToFree).Name); + AssertEquals('Empty Tag',0,TComponent(FToFree).Tag); +end; + +procedure TCJSONDeStreamer.DeStream(JSON : TJSONStringType; AObject : TObject); + +begin + FToFree:=AObject; + DS.JSONToObject(JSON,FTofree); +end; + +procedure TCJSONDeStreamer.DeStream(JSON: TJSONObject; AObject: TObject); +begin + FToFree:=AObject; + JD:=JSON; + DS.JSONToObject(JSON,FTofree); +end; + +procedure TCJSONDeStreamer.TestBoolean; + +Var + B : TBooleanComponent; + +begin + B:=TBooleanComponent.Create(Nil); + DeStream('{ "BooleanProp" : true }',B); + AssertEquals('Correct boolean value',true,B.BooleanProp); +end; + +procedure TCJSONDeStreamer.TestInteger; + +Var + B : TIntegerComponent; + +begin + B:=TIntegerComponent.Create(Nil); + DeStream('{ "IntProp" : 22 }',B); + AssertEquals('Correct integer value',22,B.IntProp); +end; + +procedure TCJSONDeStreamer.TestString; + +Var + B : TStringComponent; + +begin + B:=TStringComponent.Create(Nil); + DeStream('{ "StringProp" : "A nice string"}',B); + AssertEquals('Correct string value','A nice string',B.StringProp); +end; + +procedure TCJSONDeStreamer.TestFloat; + +Var + B : TSingleComponent; + +begin + B:=TSingleComponent.Create(Nil); + DeStream('{ "SingleProp" : 2.34 }',B); + AssertEquals('Correct single value',2.34,B.SingleProp); +end; + +procedure TCJSONDeStreamer.TestFloat2; + +Var + B : TDoubleComponent; + +begin + B:=TDoubleComponent.Create(Nil); + DeStream('{ "DoubleProp" : 3.45 }',B); + AssertEquals('Correct Double value',3.45,B.DoubleProp); +end; + +procedure TCJSONDeStreamer.TestFloat3; +Var + B : TExtendedComponent; + +begin + B:=TExtendedComponent.Create(Nil); + DeStream('{ "ExtendedProp" : 4.56 }',B); + AssertEquals('Correct extended value',4.56,B.ExtendedProp); +end; + +procedure TCJSONDeStreamer.TestFloat4; + +Var + B : TCompComponent; + +begin + B:=TCompComponent.Create(Nil); + DeStream('{ "ExtendedProp" : 5.67 }',B); +{$ifdef CPUX86_64} + AssertEquals('Correct comp value',round(5.67),B.ExtendedProp); +{$else} + AssertEquals('Correct extended value',5.67,B.ExtendedProp); +{$endif} +end; + +procedure TCJSONDeStreamer.TestFloat5; +Var + B : TCurrencyComponent; + +begin + B:=TCurrencyComponent.Create(Nil); + DeStream('{ "CurrencyProp" : 5.67 }',B); + AssertEquals('Correct string value',5.67,B.CurrencyProp); +end; + +procedure TCJSONDeStreamer.TestEnum1; + +Var + E : TEnumcomponent; + +begin + E:=TEnumComponent.Create(Nil); + DeStream('{ "Dice" : 2 }',E); + AssertEquals('Correct value',2,Ord(E.Dice)); +end; + +procedure TCJSONDeStreamer.TestEnum2; + +Var + E : TEnumcomponent; + +begin + E:=TEnumComponent.Create(Nil); + DeStream('{ "Dice" : "three" }',E); + AssertEquals('Correct value',GetEnumName(TypeInfo(TDice),Ord(Three)),GetEnumName(TypeInfo(TDice),Ord(E.Dice))); +end; + +procedure TCJSONDeStreamer.TestSet1; + +Var + T : TSetComponent; + +begin + T:=TSetComponent.Create(Nil); + DeStream('{ "Throw" : "one,two" }',T); + If not (T.Throw=[one,two]) then + Fail('Correct value for throw'); +end; + +procedure TCJSONDeStreamer.TestSet2; + +Var + T : TSetComponent; + +begin + T:=TSetComponent.Create(Nil); + DeStream('{ "Throw" : "[one,two]" }',T); + If not (T.Throw=[one,two]) then + Fail('Correct value for throw'); +end; + +procedure TCJSONDeStreamer.TestSet3; + +Var + T : TSetComponent; + +begin + T:=TSetComponent.Create(Nil); + DeStream('{ "Throw" : [ "one", "two"] }',T); + If not (T.Throw=[one,two]) then + Fail('Correct value for throw'); +end; + +procedure TCJSONDeStreamer.TestSet4; + +Var + T : TSetComponent; + +begin + T:=TSetComponent.Create(Nil); + DeStream('{ "Throw" : [ 0 , 1 ] }',T); + If not (T.Throw=[one,two]) then + Fail('Correct value for throw'); +end; + +procedure TCJSONDeStreamer.TestVariantProp; +Var + V : TVariantComponent; + +begin + V:=TVariantComponent.Create(Nil); + DeStream('{ "VariantProp" : "A string" }',V); + AssertEquals('Variant property value','A string',V.VariantProp); +end; + +procedure TCJSONDeStreamer.TestCollection; + +Var + C : TTestCollection; + +begin + C:=TTestCollection.Create; + DeStream('[ { "StrProp" : "one" }, { "StrProp" : "two" } ]',C); + AssertEquals('Item count',2,C.Count); + AssertEquals('Class item 0',TTestItem,C.Items[0].ClassType); + AssertEquals('Class item 1',TTestItem,C.Items[1].ClassType); + AssertEquals('Class item 0','one',TTestItem(C.Items[0]).StrProp); + AssertEquals('Class item 1','two',TTestItem(C.Items[1]).StrProp); +end; + +procedure TCJSONDeStreamer.TestCollection2; + +Var + C : TTestCollection; + +begin + C:=TTestCollection.Create; + DeStream('{ "Items" : [ { "StrProp" : "one" }, { "StrProp" : "two" } ] }',C); + AssertEquals('Item count',2,C.Count); + AssertEquals('Class item 0',TTestItem,C.Items[0].ClassType); + AssertEquals('Class item 1',TTestItem,C.Items[1].ClassType); + AssertEquals('Class item 0','one',TTestItem(C.Items[0]).StrProp); + AssertEquals('Class item 1','two',TTestItem(C.Items[1]).StrProp); +end; + +procedure TCJSONDeStreamer.TestCollection3; + +Var + C : TTestCollection; + +begin + C:=TTestCollection.Create; + FTofree:=C; + DS.JSONToCollection('{ "Items" : [ { "StrProp" : "one" }, { "StrProp" : "two" } ] }',C); + AssertEquals('Item count',2,C.Count); + AssertEquals('Class item 0',TTestItem,C.Items[0].ClassType); + AssertEquals('Class item 1',TTestItem,C.Items[1].ClassType); + AssertEquals('Class item 0','one',TTestItem(C.Items[0]).StrProp); + AssertEquals('Class item 1','two',TTestItem(C.Items[1]).StrProp); +end; + +procedure TCJSONDeStreamer.TestCollection4; + +Var + C : TTestCollection; + +begin + C:=TTestCollection.Create; + FTofree:=C; + DS.JSONToCollection('[ { "StrProp" : "one" }, { "StrProp" : "two" } ]',C); + AssertEquals('Item count',2,C.Count); + AssertEquals('Class item 0',TTestItem,C.Items[0].ClassType); + AssertEquals('Class item 1',TTestItem,C.Items[1].ClassType); + AssertEquals('Class item 0','one',TTestItem(C.Items[0]).StrProp); + AssertEquals('Class item 1','two',TTestItem(C.Items[1]).StrProp); +end; + +procedure TCJSONDeStreamer.TestCollection5; + +Var + C : TTestCollection; + +begin + C:=TTestCollection.Create; + FTofree:=C; + JD:=TJSONArray.Create([TJSONObject.Create(['StrProp','one']),TJSONObject.Create(['StrProp','two'])]); + DS.JSONToCollection(JD,C); + AssertEquals('Item count',2,C.Count); + AssertEquals('Class item 0',TTestItem,C.Items[0].ClassType); + AssertEquals('Class item 1',TTestItem,C.Items[1].ClassType); + AssertEquals('Class item 0','one',TTestItem(C.Items[0]).StrProp); + AssertEquals('Class item 1','two',TTestItem(C.Items[1]).StrProp); +end; + +procedure TCJSONDeStreamer.TestCollection6; +Var + C : TTestCollection; + +begin + C:=TTestCollection.Create; + FTofree:=C; + JD:=TJSONObject.Create(['Items',TJSONArray.Create([TJSONObject.Create(['StrProp','one']),TJSONObject.Create(['StrProp','two'])])]); + DS.JSONToCollection(JD,C); + AssertEquals('Item count',2,C.Count); + AssertEquals('Class item 0',TTestItem,C.Items[0].ClassType); + AssertEquals('Class item 1',TTestItem,C.Items[1].ClassType); + AssertEquals('Class item 0','one',TTestItem(C.Items[0]).StrProp); + AssertEquals('Class item 1','two',TTestItem(C.Items[1]).StrProp); +end; + +procedure TCJSONDeStreamer.TestCollectionProp; + +Var + C : TCollection; + +begin + JD:=TJSONObject.Create(['Coll',TJSONArray.Create([TJSONObject.Create(['StrProp','one']),TJSONObject.Create(['StrProp','two'])])]); + DeStream(JD as TJSONObject,TCollectionComponent.Create(Nil)); + C:=TCollectionComponent(ToFree).Coll; + AssertEquals('Item count',2,C.Count); + AssertEquals('Class item 0',TTestItem,C.Items[0].ClassType); + AssertEquals('Class item 1',TTestItem,C.Items[1].ClassType); + AssertEquals('Class item 0','one',TTestItem(C.Items[0]).StrProp); + AssertEquals('Class item 1','two',TTestItem(C.Items[1]).StrProp); +end; + +procedure TCJSONDeStreamer.TestCollectionProp2; + +Var + C : TCollection; + +begin + + DeStream('{ "Coll" : [ { "StrProp" : "one" }, { "StrProp" : "two" } ]}',TCollectionComponent.Create(Nil)); + C:=TCollectionComponent(ToFree).Coll; + AssertEquals('Item count',2,C.Count); + AssertEquals('Class item 0',TTestItem,C.Items[0].ClassType); + AssertEquals('Class item 1',TTestItem,C.Items[1].ClassType); + AssertEquals('Class item 0','one',TTestItem(C.Items[0]).StrProp); + AssertEquals('Class item 1','two',TTestItem(C.Items[1]).StrProp); +end; + +procedure TCJSONDeStreamer.TestStrings; + +Var + S : TStrings; + +begin + S:=TStringList.Create; + FTofree:=S; + DS.JSONToStrings('[ "one" , "two" ]',S); + AssertEquals('Item count',2,S.Count); + AssertEquals('First item','one',S[0]); + AssertEquals('First item','two',S[1]); +end; + +procedure TCJSONDeStreamer.TestStrings2; + +Var + S : TStrings; + +begin + S:=TStringList.Create; + FTofree:=S; + DS.JSONToStrings('{ "Strings" : [ "one" , "two" ] }',S); + AssertEquals('Item count',2,S.Count); + AssertEquals('First item','one',S[0]); + AssertEquals('First item','two',S[1]); +end; + +procedure TCJSONDeStreamer.TestStrings3; +Var + S : TStrings; + +begin + S:=TStringList.Create; + FTofree:=S; + DS.JSONToStrings('{ "Strings" : [ "one" , "two" ] }',S); + AssertEquals('Item count',2,S.Count); + AssertEquals('First item','one',S[0]); + AssertEquals('First item','two',S[1]); +end; + +{ TCJSONStreamer } + +function TCJSONStreamer.StreamObject(AObject: TObject): TJSONObject; +begin + FToFree:=AObject; + FSR:=FRJ.ObjectToJSON(AObject); + Result:=FSR; +end; + +procedure TCJSONStreamer.DoStreamProperty1(Sender: TObject; AObject: TObject; + Info: PPropInfo; var Res: TJSONData); +begin + If (info^.name<>'IntProp') and (info^.name<>'Name') and (info^.name<>'Tag') then + Fail('Wrong property'); + If (info^.name='IntProp') then + FreeAndNil(Res); + FCalled:=true; +end; + +procedure TCJSONStreamer.SetUp; +begin + Inherited; + FRJ:=TJSONStreamer.Create(Nil); +end; + +procedure TCJSONStreamer.TearDown; +begin + FreeAndNil(FSR); + FreeAndNil(FRJ); + FreeAndNil(FToFree); + Inherited; +end; + +procedure TCJSONStreamer.AssertEquals(AMessage: String; Expected, Actual: TJSONType); +begin + AssertEquals(AMessage,GetEnumName(TypeInfo(TJSONType),Ord(Expected)), + GetEnumName(TypeInfo(TJSONType),Ord(Actual))); +end; + +procedure TCJSONStreamer.AssertPropCount(ACount: Integer); +begin + AssertNotNull('Result of streaming available',FSR); + If FToFree is TComponent then + ACount:=ACount+2; // Tag + Name + Writeln(FSR.ASJSON); + AssertEquals('Property count correct',ACount,FSR.Count); +end; + +function TCJSONStreamer.AssertProperty(APropName: String; AType: TJSONType + ): TJSONData; + +Var + i : Integer; + +begin + I:=FSR.IndexOfName(APropName); + If (I=-1) then + Fail('No property "'+APropName+'" available'); + Result:=FSR.Items[i]; + AssertEquals('Property "'+APropName+'" has correct type',GetEnumName(TypeInfo(TJSONType),Ord(AType)), + GetEnumName(TypeInfo(TJSONType),Ord(Result.JSONType))); +end; + +procedure TCJSONStreamer.AssertProp(APropName: String; AValue: Boolean); +begin + AssertNotNull('Result of streaming available',FSR); + AssertEquals('Result of streaming is TJSONObject',TJSONObject,FSR.ClassType); + AssertEquals('Correct value',AValue,AssertProperty(APropName,jtBoolean).AsBoolean); +end; + +procedure TCJSONStreamer.AssertProp(APropName: String; AValue: Integer); +begin + AssertNotNull('Result of streaming available',FSR); + AssertEquals('Result of streaming is TJSONObject',TJSONObject,FSR.ClassType); + AssertEquals('Correct value',AValue,AssertProperty(APropName,jtNumber).AsInteger); +end; + +procedure TCJSONStreamer.AssertProp(APropName: String; AValue: String); +begin + AssertNotNull('Result of streaming available',FSR); + AssertEquals('Result of streaming is TJSONObject',TJSONObject,FSR.ClassType); + AssertEquals('Correct value',AValue,AssertProperty(APropName,jtString).AsString); +end; + +procedure TCJSONStreamer.AssertProp(APropName: String; AValue: TJSONFloat); +begin + AssertNotNull('Result of streaming available',FSR); + AssertEquals('Result of streaming is TJSONObject',TJSONObject,FSR.ClassType); + AssertEquals('Correct value',AValue,AssertProperty(APropName,jtNumber).AsFloat); +end; + +procedure TCJSONStreamer.AssertProp(APropName: String; AValue: array of String + ); +Var + a : TJSONArray; + i : integer; + +begin + a:=AssertArrayProp(APropName); + For I:=Low(AValue) to High(Avalue) do + begin + AssertEquals('Array element type',jtString,A.Types[i]); + AssertEquals('Array value',AValue[i],A.strings[i]); + end; +end; + +procedure TCJSONStreamer.AssertProp(APropName: String; AValue: array of Integer + ); +Var + a : TJSONArray; + i : integer; + +begin + a:=AssertArrayProp(APropName); + For I:=Low(AValue) to High(Avalue) do + begin + AssertEquals('Array element type',jtNumber,A.Types[i]); + AssertEquals('Array value',AValue[i],A.Integers[i]); + end; +end; + +function TCJSONStreamer.CreateVariantComp: TVariantComponent; +begin + Result:=TVariantComponent.Create(Nil); + FTofree:=Result; +end; + +procedure TCJSONStreamer.AssertNullProp(APropName: String); +begin + AssertProperty(APropName,jtNull); +end; + +function TCJSONStreamer.AssertObjectProp(APropName: String): TJSONObject; +begin + Result:=AssertProperty(APropName,jtObject) as TJSONObject; +end; + +function TCJSONStreamer.AssertArrayProp(APropName: String): TJSONArray; +begin + Result:=AssertProperty(APropName,jtArray) as TJSONArray; +end; + +procedure TCJSONStreamer.TestNil; +begin + AssertNull('Nil returns nil',StreamObject(Nil)); +end; + +procedure TCJSONStreamer.TestEmpty; +begin + StreamObject(TemptyPersistent.Create); + AssertPropCount(0); +end; + +procedure TCJSONStreamer.TestEmptyComponent; +begin + StreamObject(TComponent.Create(nil)); + AssertPropCount(0); +end; + +procedure TCJSONStreamer.TestWriteBoolean; + +begin + StreamObject(TBooleanComponent.Create(nil)); + AssertPropCount(1); + AssertProp('BooleanProp',False); +end; + +procedure TCJSONStreamer.TestWriteInteger; +begin + StreamObject(TIntegerComponent.Create(Nil)); + AssertPropCount(1); + AssertProp('IntProp',3); +end; + +procedure TCJSONStreamer.TestWriteString; +begin + StreamObject(TStringComponent.Create(Nil)); + AssertPropCount(1); + AssertProp('StringProp','A string'); +end; + +procedure TCJSONStreamer.TestWriteFloat; +begin + StreamObject(TSingleComponent.Create(Nil)); + AssertPropCount(1); + AssertProp('SingleProp',1.23); +end; + +procedure TCJSONStreamer.TestWriteFloat2; +begin + StreamObject(TDoubleComponent.Create(Nil)); + AssertPropCount(1); + AssertProp('DoubleProp',2.34); +end; + +procedure TCJSONStreamer.TestWriteFloat3; +begin + StreamObject(TExtendedComponent.Create(Nil)); + AssertPropCount(1); + AssertProp('ExtendedProp',3.45); +end; + +procedure TCJSONStreamer.TestWriteFloat4; +begin + StreamObject(TCompComponent.Create(Nil)); + AssertPropCount(1); + // Extended is correct, propname is wrong + {$ifdef CPUX86_64} + AssertProp('ExtendedProp',TJSONFloat(5)); + {$else} + AssertProp('ExtendedProp',4.56); + {$endif} +end; + +procedure TCJSONStreamer.TestWriteFloat5; +begin + StreamObject(TCurrencyComponent.Create(Nil)); + AssertPropCount(1); + AssertProp('CurrencyProp',5.67); +end; + +procedure TCJSONStreamer.TestEnum1; +begin + StreamObject(TEnumComponent3.Create(Nil)); + AssertPropCount(1); + AssertProp('Dice',GetEnumName(TypeInfo(TDice),Ord(three))); +end; + +procedure TCJSONStreamer.TestEnum2; +begin + RJ.Options:=[jsoEnumeratedAsInteger]; + StreamObject(TEnumComponent3.Create(Nil)); + AssertProp('Dice',Ord(three)); +end; + +procedure TCJSONStreamer.TestSet1; +begin + StreamObject(TSetComponent.Create(Nil)); + AssertPropCount(1); + AssertProp('Throw',['two','five']); +end; + +procedure TCJSONStreamer.TestSet2; +begin + RJ.Options:=[jsoSetAsString]; + StreamObject(TSetComponent.Create(Nil)); + AssertPropCount(1); + AssertProp('Throw','two,five'); +end; + +procedure TCJSONStreamer.TestSet3; +begin + RJ.Options:=[jsoSetAsString,jsoSetBrackets]; + StreamObject(TSetComponent.Create(Nil)); + AssertPropCount(1); + AssertProp('Throw','[two,five]'); +end; + +procedure TCJSONStreamer.TestSet4; +begin + RJ.Options:=[jsoSetEnumeratedAsInteger]; + StreamObject(TSetComponent.Create(Nil)); + AssertPropCount(1); + AssertProp('Throw',[Ord(two),Ord(five)]); +end; + +procedure TCJSONStreamer.TestObjectNil; + +Var + P : TOwnedComponent; + +begin + P:=TOwnedComponent.Create(Nil); + P.CompProp.Free; + P.CompProp:=Nil; + StreamObject(P); + AssertPropCount(1); + AssertNullProp('CompProp'); +end; + +procedure TCJSONStreamer.TestComponentProp1; +begin + StreamObject(TOwnedComponent.Create(Nil)); + AssertPropCount(1); + AssertProp('CompProp','SubComponent'); +end; + +procedure TCJSONStreamer.TestComponentProp2; + +Var + C : TOwnedComponent; + F : TJSONObject; + +begin + RJ.Options:=[jsoComponentsInline]; + C:=TOwnedComponent.Create(Nil); + StreamObject(C); + AssertPropCount(1); + F:=SR; + try + SR:=AssertObjectProp('CompProp'); + AssertPropCount(1); + AssertProp('Name','SubComponent'); + Assertprop('Tag',0); + AssertProp('IntProp',3); + finally + SR:=F; + end; +end; + +procedure TCJSONStreamer.TestCollectionProp1; + +Var + C : TCollectionComponent; + F : TJSONObject; + A : TJSONArray; + +begin + C:=TCollectionComponent2.Create(Nil); + StreamObject(C); + AssertPropCount(1); + F:=SR; + try + A:=AssertArrayProp('Coll'); + AssertEquals('Collection item cound',3,A.Count); + AssertEquals('Item 0 is object',jtObject,A.Types[0]); + SR:=A.Objects[0]; + FToFree:=SR; + AssertPropCount(1); + AssertProp('StrProp','First'); + AssertEquals('Item 1 is object',jtObject,A.Types[1]); + SR:=A.Objects[1]; + FToFree:=SR; + AssertPropCount(1); + AssertProp('StrProp','Second'); + AssertEquals('Item 2 is object',jtObject,A.Types[2]); + SR:=A.Objects[2]; + FToFree:=SR; + AssertPropCount(1); + AssertProp('StrProp','Third'); + finally + SR:=F; + FToFree:=C; + end; +end; + +procedure TCJSONStreamer.TestCollectionProp2; + +Var + C : TCollectionComponent; + F : TJSONObject; + A : TJSONArray; + +begin + C:=TCollectionComponent.Create(Nil); + StreamObject(C); + AssertPropCount (1); + A:=AssertArrayProp('Coll'); + AssertEquals('Collection item count',0,A.Count); +end; + +procedure TCJSONStreamer.TestPersistentProp1; + +var + P : TPersistentComponent; + F : TJSONObject; + +begin + P:=TPersistentComponent.Create(Nil); + StreamObject(P); + AssertPropCount(1); + F:=SR; + try + SR:=AssertObjectProp('Persist'); + FToFree:=P.Persist; + AssertPropCount(2); + AssertProp('AString','A persistent string'); + AssertProp('AInteger',3); + finally + FToFree:=P; + SR:=F; + end; +end; + +procedure TCJSONStreamer.TestStringsProp1; + +Var + A : TJSONArray; +begin + RJ.Options:=[jsoTstringsAsArray]; + StreamObject(TStringsCOmponent.Create(Nil)); + AssertPropCount(1); + AssertProp('StringsProp',['One','Two','Three']); +end; + +procedure TCJSONStreamer.TestStringsProp2; + +Var + A : TJSONArray; +begin + StreamObject(TStringsCOmponent.Create(Nil)); + AssertPropCount(1); + AssertProp('StringsProp','One'+sLineBreak+'Two'+sLineBreak+'Three'+sLineBreak); +end; + +procedure TCJSONStreamer.TestStringsProp3; + +Var + O : TJSONObject; + S : TStringsComponent; + +begin + S:=TStringsCOmponent.Create(Nil); + RJ.Options:=[jsoTstringsAsObject]; + StreamObject(S); + AssertPropCount(1); + O:=SR; + SR:=AssertObjectprop('StringsProp'); + FTofree:=Nil; + try + AssertNullProp('One'); + AssertNullProp('Two'); + AssertNullProp('Three'); + finally + SR:=o; + FToFree:=S; + end; +end; + +procedure TCJSONStreamer.TestStringsProp4; + +Var + O,SP : TJSONObject; + S : TStringsComponent; + +begin + S:=TStringsCOmponent.Create(Nil); + S.StringsProp.Objects[0]:=TEmptyPersistent.Create; + S.StringsProp.Objects[1]:=TEmptyPersistent.Create; + S.StringsProp.Objects[2]:=TEmptyPersistent.Create; + try + RJ.Options:=[jsoTstringsAsObject]; + StreamObject(S); + AssertPropCount(1); + O:=SR; + SP:=AssertObjectprop('StringsProp'); + SR:=SP; + FTofree:=Nil; + try + SR:=AssertObjectProp('One'); + AssertPropCount(0); + SR:=SP; + SR:=AssertObjectProp('Two'); + AssertPropCount(0); + SR:=SP; + SR:=AssertObjectProp('Three'); + AssertPropCount(0); + finally + SR:=o; + FToFree:=S; + end; + finally + S.StringsProp.Objects[0].Free; + S.StringsProp.Objects[1].Free; + S.StringsProp.Objects[2].Free; + end; +end; + +procedure TCJSONStreamer.TestStringsArray; + +Var + O : TJSONArray; + S : TStringList; + +begin + S:=TStringList.create; + try + S.Add('one'); + O:=RJ.StreamTStringsArray(S); + try + AssertEquals('one element',1,O.Count); + AssertEquals('string type',jtString,O.Types[0]); + AssertEquals('string value','one',O.Strings[0]); + finally + FreeAndNil(O); + end; + finally + S.Free; + end; +end; + +procedure TCJSONStreamer.TestStringsObject; + +Var + O : TJSONObject; + S : TStringList; + +begin + S:=TStringList.create; + try + S.AddObject('one',TEmptyPersistent.Create); + O:=RJ.StreamTStringsObject(S); + try + AssertEquals('one element',1,O.Count); + AssertEquals('Have property',0,O.IndexOfName('one')); + AssertEquals('string type',jtObject,O.Types['one']); + AssertEquals('string value','one',O.Names[0]); + finally + FreeAndNil(O); + end; + finally + S.Objects[0].FRee; + S.Free; + end; +end; + +procedure TCJSONStreamer.TestStringsStream1; + +Var + D: TJSONData; + O : TJSONArray; + S : TStringList; + +begin + S:=TStringList.create; + try + S.Add('one'); + RJ.Options:=[jsoTstringsAsArray]; + D:=RJ.StreamTStrings(S); + try + AssertEquals('Correct type',jtArray,D.JSONType); + O:=D as TJSONArray; + AssertEquals('one element',1,O.Count); + AssertEquals('string type',jtString,O.Types[0]); + AssertEquals('string value','one',O.Strings[0]); + finally + FreeAndNil(O); + end; + finally + S.Free; + end; +end; + +procedure TCJSONStreamer.TestStringsStream2; + +Var + D : TJSONData; + O : TJSONObject; + S : TStringList; + +begin + S:=TStringList.create; + try + S.AddObject('one',TEmptyPersistent.Create); + RJ.Options:=[jsoTstringsAsObject]; + D:=RJ.StreamTstrings(S); + try + AssertEquals('Correct type',jtObject,D.JSONType); + O:=D as TJSONObject; + AssertEquals('one element',1,O.Count); + AssertEquals('Have property',0,O.IndexOfName('one')); + AssertEquals('string type',jtObject,O.Types['one']); + AssertEquals('string value','one',O.Names[0]); + finally + SR:=O; + end; + finally + S.Objects[0].FRee; + S.Free; + end; +end; + +procedure TCJSONStreamer.TestStringsStream3; +Var + O : TJSONObject; + S : TStringList; + +begin + S:=TStringList.create; + try + S.AddObject('one',TEmptyPersistent.Create); + RJ.Options:=[jsoTstringsAsObject]; + SR:=RJ.ObjectToJSON(S); + O:=AssertObjectProp('Strings'); + AssertEquals('one element',1,O.Count); + AssertEquals('Have property',0,O.IndexOfName('one')); + AssertEquals('string type',jtObject,O.Types['one']); + AssertEquals('string value','one',O.Names[0]); + finally + S.Objects[0].FRee; + S.Free; + end; +end; + +procedure TCJSONStreamer.TestStringsStream4; +Var + O : TJSONObject; + S : TStringList; + +begin + S:=TStringList.create; + try + S.AddObject('one',TEmptyPersistent.Create); + SR:=RJ.ObjectToJSON(S); + AssertProp('Strings','one'+sLinebreak); + finally + S.Objects[0].FRee; + S.Free; + end; +end; + +procedure TCJSONStreamer.TestStringsStream5; +Var + D : TJSONData; + S : TStringList; + +begin + S:=TStringList.create; + try + S.AddObject('one',TEmptyPersistent.Create); + D:=RJ.StreamTstrings(S); + try + AssertEquals('String data',jtString,D.JSONType); + AssertEquals('String value','one'+sLineBreak,D.AsString); + finally + D.free; + end; + finally + S.Objects[0].FRee; + S.Free; + end; +end; + +procedure TCJSONStreamer.TestCollectionStream; + +Var + C : TTestCollection; + A : TJSONArray; + +begin + C:=TTestCollection.Create; + FToFree:=C; + TTestItem(C.Add).StrProp:='One'; + TTestItem(C.Add).StrProp:='Two'; + A:=RJ.StreamCollection(C); + try + AssertNotNull('Have result',A); + AssertEquals('2 items',2,A.Count); + AssertEquals('Type item 0,',jtObject,A.Types[0]); + AssertEquals('Type item 1,',jtObject,A.Types[1]); + SR:=A.Objects[0]; + AssertPropCount(1); + AssertProp('StrProp','One'); + SR:=A.Objects[1]; + AssertPropCount(1); + AssertProp('StrProp','Two'); + SR:=Nil; + finally + FreeAndNil(A); + end; +end; + +procedure TCJSONStreamer.TestCollectionStream2; + +Var + C : TTestCollection; + A : TJSONArray; + o : TJSONObject; + +begin + C:=TTestCollection.Create; + TTestItem(C.Add).StrProp:='One'; + TTestItem(C.Add).StrProp:='Two'; + FToFree:=C; + StreamObject(C); + O:=SR; + try + A:=AssertProperty('Items',jtArray) as TJSONArray; + AssertNotNull('Have result',A); + AssertEquals('2 items',2,A.Count); + AssertEquals('Type item 0,',jtObject,A.Types[0]); + AssertEquals('Type item 1,',jtObject,A.Types[1]); + SR:=A.Objects[0]; + AssertPropCount(1); + AssertProp('StrProp','One'); + SR:=A.Objects[1]; + AssertPropCount(1); + AssertProp('StrProp','Two'); + SR:=Nil; + finally + SR:=O; + end; +end; + +procedure TCJSONStreamer.TestOnStreamProperty; +begin + RJ.OnStreamProperty:=@DoStreamProperty1; + StreamObject(TIntegerComponent.Create(Nil)); + AssertPropCount(0); +end; + +procedure TCJSONStreamer.TestDateTimeProp; + +Var + D : Double; +begin + StreamObject(TDateTimeComponent.Create(Nil)); + D:=EncodeDate(1996,8,1); + AssertPropCount(1); + AssertProp('DateTimeProp',D); +end; + +procedure TCJSONStreamer.TestDateTimeProp2; +Var + D : Double; +begin + StreamObject(TDateTimeComponent2.Create(Nil)); + D:=EncodeTime(23,20,0,0); + AssertPropCount(1); + AssertProp('DateTimeProp',D); +end; + +procedure TCJSONStreamer.TestDateTimeProp3; +Var + D : Double; +begin + StreamObject(TDateTimeComponent3.Create(Nil)); + D:=EncodeDate(1996,8,1)+EncodeTime(23,20,0,0); + AssertPropCount(1); + AssertProp('DateTimeProp',D); +end; + +procedure TCJSONStreamer.TestDateTimeProp4; + +begin + RJ.Options:=[jsoDateTimeAsString]; + StreamObject(TDateTimeComponent.Create(Nil)); + AssertPropCount(1); + AssertProp('DateTimeProp',DateToStr(EncodeDate(1996,8,1))); +end; + +procedure TCJSONStreamer.TestDateTimeProp5; + +begin + RJ.Options:=[jsoDateTimeAsString]; + StreamObject(TDateTimeComponent2.Create(Nil)); + AssertPropCount(1); + AssertProp('DateTimeProp',TimeToStr(EncodeTime(23,20,0,0))); +end; + +procedure TCJSONStreamer.TestDateTimeProp6; + +begin + RJ.Options:=[jsoDateTimeAsString]; + StreamObject(TDateTimeComponent3.Create(Nil)); + AssertPropCount(1); + AssertProp('DateTimeProp',DateTimeToStr(EncodeDate(1996,8,1)+EncodeTime(23,20,0,0))); +end; + +procedure TCJSONStreamer.TestDateTimeProp7; +begin + RJ.Options:=[jsoDateTimeAsString]; + RJ.DateTimeFormat:='hh:nn'; + StreamObject(TDateTimeComponent3.Create(Nil)); + AssertPropCount(1); + AssertProp('DateTimeProp',FormatDateTime('hh:nn',EncodeDate(1996,8,1)+EncodeTime(23,20,0,0))); +end; + +procedure TCJSONStreamer.TestVariantShortint; + +Var + i : ShortInt; + C : TVariantComponent; + +begin + i:=3; + C:=CreateVariantComp; + C.VariantProp:=i; + AssertEquals('Variant type',VarTypeAsText(varshortint),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp',3); +end; + +procedure TCJSONStreamer.TestVariantbyte; +Var + i : Byte; + C : TVariantComponent; + +begin + i:=3; + C:=CreateVariantComp; + C.VariantProp:=i; + AssertEquals('Variant type',VarTypeAsText(varByte),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp',3); +end; + +procedure TCJSONStreamer.TestVariantword; + +Var + i : Word; + C : TVariantComponent; + +begin + i:=3; + C:=CreateVariantComp; + C.VariantProp:=i; + AssertEquals('Variant type',VarTypeAsText(varWord),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp',3); +end; + +procedure TCJSONStreamer.TestVariantsmallint; + +Var + i : Smallint; + C : TVariantComponent; + +begin + i:=3; + C:=CreateVariantComp; + C.VariantProp:=i; + AssertEquals('Variant type',VarTypeAsText(varSmallint),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp',3); +end; + +procedure TCJSONStreamer.TestVariantinteger; +Var + i : Integer; + C : TVariantComponent; + +begin + i:=3; + C:=CreateVariantComp; + C.VariantProp:=i; + AssertEquals('Variant type',VarTypeAsText(varInteger),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp',3); +end; + +procedure TCJSONStreamer.TestVariantlongword; + +Var + i : Cardinal; + C : TVariantComponent; + +begin + i:=3; + C:=CreateVariantComp; + C.VariantProp:=i; + AssertEquals('Variant type',VarTypeAsText(varLongword),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp',3); +end; + +procedure TCJSONStreamer.TestVariantint64; +Var + i : Int64; + C : TVariantComponent; + +begin + i:=3; + C:=CreateVariantComp; + C.VariantProp:=i; + AssertEquals('Variant type',VarTypeAsText(varInt64),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp',3); +end; + +procedure TCJSONStreamer.TestVariantqword; +Var + i : QWord; + C : TVariantComponent; + +begin + i:=3; + C:=CreateVariantComp; + C.VariantProp:=i; + AssertEquals('Variant type',VarTypeAsText(varQWord),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp',3); +end; + +procedure TCJSONStreamer.TestVariantsingle; +Var + i : Single; + C : TVariantComponent; + +begin + i:=3.14; + C:=CreateVariantComp; + C.VariantProp:=VarAsType(3.14,varSingle); + AssertEquals('Variant type',VarTypeAsText(varSingle),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp',3.14); +end; + +procedure TCJSONStreamer.TestVariantdouble; + +Var + i : Double; + C : TVariantComponent; + +begin + i:=3.14; + C:=CreateVariantComp; + C.VariantProp:=i; + AssertEquals('Variant type',VarTypeAsText(varDouble),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp',3.14); +end; + +procedure TCJSONStreamer.TestVariantCurrency; +Var + i : Currency; + C : TVariantComponent; + +begin + i:=3.14; + C:=CreateVariantComp; + C.VariantProp:=i; + AssertEquals('Variant type',VarTypeAsText(varCurrency),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp',3.14); +end; + +procedure TCJSONStreamer.TestVariantString; + +Var + i : String; + C : TVariantComponent; + +begin + i:='3.14'; + C:=CreateVariantComp; + C.VariantProp:=i; + AssertEquals('Variant type',VarTypeAsText(varString),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp','3.14'); +end; + +procedure TCJSONStreamer.TestVariantolestr; + +Var + i : String; + C : TVariantComponent; + +begin + i:='3.14'; + C:=CreateVariantComp; + C.VariantProp:=VarAsType(i,varOleStr); + AssertEquals('Variant type',VarTypeAsText(varOleStr),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp','3.14'); +end; + +procedure TCJSONStreamer.TestVariantboolean; +Var + i : Boolean; + C : TVariantComponent; + +begin + i:=True; + C:=CreateVariantComp; + C.VariantProp:=i; + AssertEquals('Variant type',VarTypeAsText(varBoolean),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp',True); +end; + +procedure TCJSONStreamer.TestVariantDate; + +Var + i : TDateTime; + C : TVariantComponent; + +begin + i:=EncodeDate(2010,12,23); + C:=CreateVariantComp; + C.VariantProp:=i; + AssertEquals('Variant type',VarTypeAsText(varDate),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp',EncodeDate(2010,12,23)); +end; + +procedure TCJSONStreamer.TestVariantDate2; + +Var + i : TDateTime; + C : TVariantComponent; + +begin + RJ.Options:=[jsoDateTimeAsString]; + i:=EncodeDate(2010,12,23); + C:=CreateVariantComp; + C.VariantProp:=i; + AssertEquals('Variant type',VarTypeAsText(varDate),VarTypeAsText(VarType(C.VariantProp))); + StreamObject(FTofree); + AssertPropCount(1); + AssertProp('VariantProp',DateToStr(EncodeDate(2010,12,23))); +end; + +procedure TCJSONStreamer.TestVariantArray; +Var + i : Integer; + V : Variant; + C : TVariantComponent; + A : TJSONArray; + +begin + V:=VarArrayCreate([1,10],varInteger); + For I:=1 to 10 do + V[i]:=11-I; + C:=CreateVariantComp; + C.VariantProp:=V; + StreamObject(FTofree); + AssertPropCount(1); + A:=AssertProperty('VariantProp',jtArray) as TJSONArray; + AssertEquals('10 elements in array',10,A.Count); + For I:=1 to 10 do + begin + assertEquals('Type of element',jtNumber,A.Types[i-1]); + AssertEquals('Value of element',11-i,A.Integers[i-1]); + end; +end; + +procedure TCJSONStreamer.TestMultipleProps; +begin + StreamObject(TMultipleComponent.Create(Nil)); + AssertPropCount(5); + AssertProp('IntProp',1); + Assertprop('StringProp','A String'); + AssertProp('CurrencyProp',2.3); + AssertProp('Throw',['three','four']); + AssertProp('Dice','two'); +end; + +procedure TCJSONStreamer.TestObjectToJSONString; +begin + StreamObject(TIntegerComponent.Create(Nil)); + AssertEquals('Correct stream',SR.AsJSON,RJ.ObjectToJSONString(FToFree)); +end; + +procedure TCJSONStreamer.TestStringsToJSONString; +Var + S : TStrings; +begin + S:=TStringList.Create; + try + S.Add('one'); + S.Add('two'); + S.Add('three'); + AssertEquals('StringsToJSONString','["one", "two", "three"]',RJ.StringsToJSON(S)); + AssertEquals('StringsToJSONString','{ "one" : null, "two" : null, "three" : null }',RJ.StringsToJSON(S,True)); + finally + FreeAndNil(S); + end; +end; + +procedure TCJSONStreamer.TestCollectionToJSONString; + +Var + C : TTestCollection; + +begin + C:=TTestCollection.Create; + try + (C.Add as TTestItem).StrProp:='one'; + (C.Add as TTestItem).StrProp:='two'; + (C.Add as TTestItem).StrProp:='three'; + AssertEquals('CollectionToJSON','[{ "StrProp" : "one" }, { "StrProp" : "two" }, { "StrProp" : "three" }]',RJ.CollectionToJSON(C)); + finally + FreeAndNil(C); + end; +end; + +procedure TCJSONStreamer.TestChildren; + +Var + C : TChildrenComponent; + +begin + C:=TChildrenComponent.Create(Nil); + TComponent.Create(C).Name:='Child1'; + TComponent.Create(C).Name:='Child2'; + StreamObject(C); + If SR.IndexOfName('Children')<>-1 then + Fail('Children streamed with default options'); + +end; + +procedure TCJSONStreamer.TestChildren2; +Var + C : TChildrenComponent; + A : TJSONArray; + O : TJSONObject; + +begin + C:=TChildrenComponent.Create(Nil); + TComponent.Create(C).Name:='Child1'; + TComponent.Create(C).Name:='Child2'; + RJ.Options:=[jsoStreamChildren]; + StreamObject(C); + AssertPropCount(1); + A:=AssertProperty('Children',jtArray) as TJSONArray; + O:=SR; + try + AssertEquals('2 Elements in array',2,A.Count); + AssertEquals('First in array is object',jtObject,A.Types[0]); + AssertEquals('Second in array is object',jtObject,A.Types[1]); + SR:=A.Objects[0]; + AssertProp('Name','Child1'); + SR:=A.Objects[1]; + AssertProp('Name','Child2'); + finally + SR:=O; + end; +end; + +initialization + + RegisterTests([TCJSONStreamer,TCJSONDeStreamer]); +end. + |
