fpjsonrtti.pp 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178
  1. unit fpjsonrtti;
  2. {$mode objfpc}
  3. interface
  4. uses
  5. Classes, SysUtils, contnrs, typinfo, fpjson, rttiutils, jsonparser;
  6. Const
  7. RFC3339DateTimeFormat = 'yyyy"-"mm"-"dd"T"hh":"nn":"ss';
  8. RFC3339DateTimeFormatMsec = RFC3339DateTimeFormat+'.zzz';
  9. Type
  10. TJSONStreamEvent = Procedure (Sender : TObject; AObject : TObject; JSON : TJSONObject) of object;
  11. TJSONPropertyEvent = Procedure (Sender : TObject; AObject : TObject; Info : PPropInfo; var Res : TJSONData) of object;
  12. TJSONStreamOption = (jsoStreamChildren, // If set, children will be streamed in 'Children' Property
  13. jsoEnumeratedAsInteger, // Write enumerated as integer. Default is string.
  14. jsoSetAsString, // Write Set as a string. Default is an array.
  15. jsoSetEnumeratedAsInteger, // Write enumerateds in set array as integers.
  16. jsoSetBrackets, // Use brackets when creating set as array
  17. jsoComponentsInline, // Always stream components inline. Default is to stream name, unless csSubcomponent in ComponentStyle
  18. jsoTStringsAsArray, // Stream TStrings as an array of strings. Associated objects are not streamed.
  19. jsoTStringsAsObject, // Stream TStrings as an object : string = { object }
  20. jsoDateTimeAsString, // Format a TDateTime value as a string
  21. jsoUseFormatString, // Use FormatString when creating JSON strings.
  22. jsoCheckEmptyDateTime, // If TDateTime value is empty and jsoDateTimeAsString is used, 0 date returns empty string
  23. jsoLegacyDateTime, // Set this to enable old date/time formatting. Current behaviour is to save date/time as a ISO 9601 value.
  24. jsoLowerPropertyNames, // Set this to force lowercase names when streaming to JSON.
  25. jsoStreamTList // Set this to assume that TList contains a list of TObjects. Use with care!
  26. );
  27. TJSONStreamOptions = Set of TJSONStreamOption;
  28. TJSONFiler = Class(TComponent)
  29. Protected
  30. Procedure Error(Const Msg : String);
  31. Procedure Error(Const FMT : String; Args : Array of const);
  32. end;
  33. { TJSONStreamer }
  34. TJSONStreamer = Class(TJSONFiler)
  35. private
  36. FAfterStreamObject: TJSONStreamEvent;
  37. FBeforeStreamObject: TJSONStreamEvent;
  38. FChildProperty: String;
  39. FDateTimeFormat: String;
  40. FOnStreamProperty: TJSONPropertyEvent;
  41. FOptions: TJSONStreamOptions;
  42. function GetChildProperty: String;
  43. function IsChildStored: boolean;
  44. function StreamChildren(AComp: TComponent): TJSONArray;
  45. protected
  46. function StreamClassProperty(Const AObject: TObject): TJSONData; virtual;
  47. Function StreamProperty(Const AObject : TObject; Const PropertyName : String) : TJSONData;
  48. Function StreamProperty(Const AObject : TObject; PropertyInfo : PPropInfo) : TJSONData;
  49. Function FormatDateProp(const DateTime : TDateTime) : TJSONString;
  50. Public
  51. Constructor Create(AOwner : TComponent); override;
  52. Destructor Destroy;override;
  53. //
  54. // Basic functions
  55. //
  56. // Use RTTI to stream object.
  57. // If AObject is of type TStrings or TCollection, special treatment occurs:
  58. // TStrings results in { Strings: [S,S,S] } or { Strings: { "S1" : O1, "S2" : O2 }} depending on Options.
  59. // Collection results in { Items: [I,I,I] }
  60. Function ObjectToJSON(Const AObject : TObject) : TJSONObject;
  61. // Stream a collection - always returns an array
  62. function StreamCollection(Const ACollection: TCollection): TJSONArray;
  63. // Stream an objectlist - always returns an array
  64. function StreamObjectList(Const AnObjectList: TObjectList): TJSONArray;
  65. // Stream a List - always returns an array
  66. function StreamTList(Const AList: TList): TJSONArray;
  67. // Stream a TStrings instance as an array
  68. function StreamTStringsArray(Const AStrings: TStrings): TJSONArray;
  69. // Stream a TStrings instance as an object
  70. function StreamTStringsObject(Const AStrings: TStrings): TJSONObject;
  71. // Stream a TStrings instance. Takes into account Options.
  72. function StreamTStrings(Const AStrings: TStrings): TJSONData;
  73. // Stream a variant as JSON.
  74. function StreamVariant(const Data: Variant): TJSONData; virtual;
  75. //
  76. // Some utility functions.
  77. //
  78. // Call ObjectToJSON and convert result to JSON String.
  79. Function ObjectToJSONString(AObject : TObject) : TJSONStringType;
  80. // Convert TSTrings to JSON string with array or Object.
  81. Function StringsToJSON(Const Strings : TStrings; AsObject : Boolean = False) : TJSONStringType;
  82. // Convert collection to JSON string
  83. Function CollectionToJSON(Const ACollection : TCollection) : TJSONStringType;
  84. // Convert variant to JSON String
  85. Function VariantToJSON(Const Data : Variant) : TJSONStringType;
  86. Published
  87. // Format used when formatting DateTime values. Only used in conjunction with jsoDateTimeToString
  88. Property DateTimeFormat : String Read FDateTimeFormat Write FDateTimeFormat;
  89. // Options to use when streaming
  90. Property Options : TJSONStreamOptions Read FOptions Write FOptions;
  91. // Called before streaming an object with ObjectToJSON
  92. Property BeforeStreamObject : TJSONStreamEvent Read FBeforeStreamObject Write FBeforeStreamObject;
  93. // Called After streaming an object with ObjectToJSON
  94. Property AfterStreamObject : TJSONStreamEvent Read FAfterStreamObject Write FAfterStreamObject;
  95. // Called whenever a property was streamed. If Res is nil on return, no property is added.
  96. Property OnStreamProperty : TJSONPropertyEvent Read FOnStreamProperty Write FOnStreamProperty;
  97. // Property name to use when streaming child components. Default is "Children"
  98. Property ChildProperty : String Read GetChildProperty Write FChildProperty Stored IsChildStored;
  99. end;
  100. { TJSONDeStreamer }
  101. TJSONRestorePropertyEvent = Procedure (Sender : TObject; AObject : TObject; Info : PPropInfo; AValue : TJSONData; Var Handled : Boolean) of object;
  102. TJSONPropertyErrorEvent = Procedure (Sender : TObject; AObject : TObject; Info : PPropInfo; AValue : TJSONData; Error : Exception; Var Continue : Boolean) of object;
  103. TJSONGetObjectEvent = Procedure (Sender : TOBject; AObject : TObject; Info : PPropInfo; AData : TJSONObject; DataName : TJSONStringType; Var AValue : TObject);
  104. TJSONDestreamOption = (jdoCaseInsensitive,jdoIgnorePropertyErrors);
  105. TJSONDestreamOptions = set of TJSONDestreamOption;
  106. TJSONDeStreamer = Class(TJSONFiler)
  107. private
  108. FAfterReadObject: TJSONStreamEvent;
  109. FBeforeReadObject: TJSONStreamEvent;
  110. FDateTimeFormat: String;
  111. FOnGetObject: TJSONGetObjectEvent;
  112. FOnPropError: TJSONpropertyErrorEvent;
  113. FOnRestoreProp: TJSONRestorePropertyEvent;
  114. FCaseInsensitive : Boolean;
  115. FOptions: TJSONDestreamOptions;
  116. procedure DeStreamClassProperty(AObject: TObject; PropInfo: PPropInfo; PropData: TJSONData);
  117. function GetCaseInsensitive: Boolean;
  118. procedure SetCaseInsensitive(AValue: Boolean);
  119. protected
  120. // Try to parse a date.
  121. Function ExtractDateTime(S : String): TDateTime;
  122. function GetObject(AInstance : TObject; const APropName: TJSONStringType; D: TJSONObject; PropInfo: PPropInfo): TObject;
  123. procedure DoRestoreProperty(AObject: TObject; PropInfo: PPropInfo; PropData: TJSONData); virtual;
  124. function DoMapProperty(AObject: TObject; PropInfo: PPropInfo; JSON: TJSONObject): TJSONData; virtual;
  125. procedure DoBeforeReadObject(Const JSON: TJSONObject; AObject: TObject); virtual;
  126. procedure DoAfterReadObject(Const JSON: TJSONObject; AObject: TObject); virtual;
  127. Function ObjectFromString(Const JSON : TJSONStringType) : TJSONData; virtual;
  128. procedure RestoreProperty(AObject: TObject; PropInfo: PPropInfo; PropData: TJSONData);
  129. Public
  130. Constructor Create(AOwner : TComponent); override;
  131. Destructor Destroy; override;
  132. // Convert JSON object to properties of AObject
  133. Procedure JSONToObject(Const JSON : TJSONStringType; AObject : TObject);
  134. Procedure JSONToObject(Const JSON : TJSONObject; AObject : TObject);
  135. // Convert JSON object/array to collection.
  136. Procedure JSONToCollection(Const JSON : TJSONStringType; ACollection : TCollection);
  137. Procedure JSONToCollection(Const JSON : TJSONData; ACollection : TCollection);
  138. // Convert JSON array/object/string to TStrings
  139. Procedure JSONToStrings(Const JSON : TJSONStringType; AStrings : TSTrings);
  140. Procedure JSONToStrings(Const JSON : TJSONData; AStrings : TSTrings);
  141. // Convert JSON data to a variant. Supports simple data types and arrays.
  142. Function JSONToVariant(Data: TJSONData): Variant;
  143. Function JSONToVariant(Data: TJSONStringType): Variant;
  144. // Triggered at the start of each call to JSONToObject
  145. Property BeforeReadObject : TJSONStreamEvent Read FBeforeReadObject Write FBeforeReadObject;
  146. // Triggered at the end of each call to JSONToObject (not if exception happens)
  147. Property AfterReadObject : TJSONStreamEvent Read FAfterReadObject Write FAfterReadObject;
  148. // Called when a property will be restored. If 'Handled' is True on return, property is considered restored.
  149. Property OnRestoreProperty : TJSONRestorePropertyEvent Read FOnRestoreProp Write FOnRestoreProp;
  150. // Called when an error occurs when restoring a property. If Continue is False on return, exception is re-raised.
  151. Property OnPropertyError : TJSONpropertyErrorEvent Read FOnPropError Write FOnPropError;
  152. // Called when a object-typed property must be restored, and the property is Nil. Must return an instance for the property.
  153. // Published Properties of the instance will be further restored with available data.
  154. Property OngetObject : TJSONGetObjectEvent Read FOnGetObject Write FOnGetObject;
  155. // JSON is by definition case sensitive. Should properties be looked up case-insentive ?
  156. Property CaseInsensitive : Boolean Read GetCaseInsensitive Write SetCaseInsensitive ; deprecated;
  157. // DateTime format. If not set, RFC3339DateTimeFormat is assumed.
  158. // If set, it will be used as an argument to ScanDateTime. If that fails, StrToDateTime is used.
  159. Property DateTimeFormat : String Read FDateTimeFormat Write FDateTimeFormat;
  160. // Options overning the behaviour
  161. Property Options : TJSONDestreamOptions Read FOptions Write FOptions;
  162. end;
  163. EJSONRTTI = Class(Exception);
  164. implementation
  165. uses dateutils, variants, rtlconsts;
  166. ResourceString
  167. SErrUnknownPropertyKind = 'Unknown property kind for property : "%s"';
  168. SErrUnsupportedPropertyKind = 'Unsupported property kind for property: "%s"';
  169. SErrUnsupportedVariantType = 'Unsupported variant type : %d';
  170. SErrUnsupportedArrayType = 'JSON array cannot be streamed to object of class "%s"';
  171. SErrUnsupportedJSONType = 'Cannot destream object from JSON data of type "%s"';
  172. SErrUnsupportedCollectionType = 'Unsupported JSON type for collections: "%s"';
  173. SErrUnsupportedCollectionItemType = 'Array element %d is not a valid type for a collection item: "%s"';
  174. SErrUnsupportedStringsItemType = 'Array element %d is not a valid type for a stringlist item: "%s"';
  175. SErrUnsupportedStringsType = 'Unsupported JSON type for stringlists: "%s"';
  176. SErrUnsupportedStringsObjectType = 'Object Element %s is not a valid type for a stringlist object: "%s"';
  177. SErrUnSupportedEnumDataType = 'Unsupported JSON type for enumerated property "%s" : "%s"';
  178. SErrUnsupportedVariantJSONType = 'Unsupported JSON type for variant value : "%s"';
  179. SErrUnsupportedObjectData = 'Unsupported JSON type for object property: "%s"';
  180. { TStreamChildrenHelper }
  181. Type
  182. TSet = set of 0..31; // Used to (de)stream set properties.
  183. TStreamChildrenHelper = Class
  184. Private
  185. FChildren : TJSONArray;
  186. FStreamer:TJSONStreamer;
  187. procedure StreamChild(AChild: TComponent);
  188. public
  189. Function StreamChildren(AComponent : TComponent; AStreamer : TJSONStreamer): TJSONArray;
  190. end;
  191. THackComponent = Class(TComponent);
  192. { TJSONDeStreamer }
  193. function TJSONDeStreamer.ObjectFromString(const JSON: TJSONStringType): TJSONData;
  194. begin
  195. With TJSONParser.Create(JSON) do
  196. try
  197. Result:=Parse;
  198. finally
  199. Free;
  200. end;
  201. end;
  202. constructor TJSONDeStreamer.Create(AOwner: TComponent);
  203. begin
  204. inherited Create(AOwner);
  205. end;
  206. destructor TJSONDeStreamer.Destroy;
  207. begin
  208. inherited Destroy;
  209. end;
  210. procedure TJSONDeStreamer.JSONToObject(const JSON: TJSONStringType;
  211. AObject: TObject);
  212. Var
  213. D : TJSONData;
  214. begin
  215. D:=ObjectFromString(JSON);
  216. if not Assigned(D) then
  217. Exit;
  218. try
  219. If D.JSONType=jtObject then
  220. JSONToObject(D as TJSONObject,AObject)
  221. else if D.JSONType=jtArray then
  222. begin
  223. If AObject is TStrings then
  224. JSONToStrings(D,AObject as TSTrings)
  225. else if AObject is TCollection then
  226. JSONTOCollection(D,AObject as TCollection)
  227. else
  228. Error(SErrUnsupportedArrayType,[AObject.ClassName])
  229. end
  230. else if (D.JSONType=jtString) and (AObject is TStrings) then
  231. JSONToStrings(D,AObject as TStrings)
  232. else
  233. Error(SErrUnsupportedJSONType,[JSONTypeName(D.JSONType)]);
  234. finally
  235. FreeAndNil(D);
  236. end;
  237. end;
  238. function TJSONDeStreamer.JSONToVariant(Data: TJSONData): Variant;
  239. Var
  240. I : integer;
  241. begin
  242. Case Data.JSONType of
  243. jtNumber :
  244. Case TJSONNumber(Data).NumberType of
  245. ntFloat : Result:=Data.AsFloat;
  246. ntInteger : Result:=Data.AsInteger;
  247. ntInt64 : Result:=Data.Asint64;
  248. ntQWord : Result:=Data.AsQWord;
  249. end;
  250. jtString :
  251. Result:=Data.AsString;
  252. jtBoolean:
  253. Result:=Data.AsBoolean;
  254. jtNull:
  255. Result:=Null;
  256. jtArray :
  257. begin
  258. Result:=VarArrayCreate([0,Data.Count-1],varVariant);
  259. For I:=0 to Data.Count-1 do
  260. Result[i]:=JSONToVariant(Data.Items[i]);
  261. end;
  262. else
  263. Error(SErrUnsupportedVariantJSONType,[GetEnumName(TypeInfo(TJSONType),Ord(Data.JSONType))]);
  264. end;
  265. end;
  266. function TJSONDeStreamer.JSONToVariant(Data: TJSONStringType): Variant;
  267. Var
  268. D : TJSONData;
  269. begin
  270. D:=ObjectFromString(Data);
  271. try
  272. Result:=JSONToVariant(D);
  273. finally
  274. D.Free;
  275. end;
  276. end;
  277. procedure TJSONDeStreamer.DeStreamClassProperty(AObject : TObject;PropInfo : PPropInfo; PropData : TJSONData);
  278. Var
  279. O : TObject;
  280. begin
  281. O:=GetObjectProp(AObject,PropInfo);
  282. If O is TStrings then
  283. JSONToStrings(PropData,O as TStrings)
  284. else if (O is TCollection) then
  285. JSONToCollection(PropData,O as TCollection)
  286. else
  287. begin
  288. If (O=Nil) then
  289. begin
  290. If (PropData.JSONType=jtString) then
  291. O:=GetObject(AObject,PropData.AsString,Nil,PropInfo)
  292. else if (PropData.JSONType=jtObject) then
  293. O:=GetObject(AObject,'',PropData as TJSONObject,PropInfo)
  294. else
  295. Error(SErrUnsupportedObjectData,[JsonTypeName(PropData.JSONType){GetEnumName(TypeInfo(TJSONType),Ord(PropData.JSONType))}]);
  296. SetObjectProp(AObject,PropInfo,O);
  297. end;
  298. If (O<>Nil) and (PropData.JSONType=jtObject) then
  299. JSONToObject(PropData as TJSONObject,O);
  300. end;
  301. end;
  302. function TJSONDeStreamer.GetCaseInsensitive: Boolean;
  303. begin
  304. Result:=jdoCaseInsensitive in Options;
  305. end;
  306. procedure TJSONDeStreamer.SetCaseInsensitive(AValue: Boolean);
  307. begin
  308. if AValue then
  309. Include(Foptions,jdoCaseInsensitive)
  310. else
  311. Exclude(Foptions,jdoCaseInsensitive);
  312. end;
  313. function TJSONDeStreamer.ExtractDateTime(S: String): TDateTime;
  314. Var
  315. Fmt : String;
  316. E,fmtSpecified : Boolean;
  317. begin
  318. E:=False;
  319. FMT:=DateTimeFormat;
  320. fmtSpecified:=Fmt<>'';
  321. if Not fmtSpecified then
  322. FMT:=RFC3339DateTimeFormat;
  323. Try
  324. // No TryScanDateTime
  325. Result:=ScanDatetime(FMT,S);
  326. except
  327. if fmtSpecified then
  328. Raise
  329. else
  330. E:=True;
  331. end;
  332. if E then
  333. if not TryStrToDateTime(S,Result) then
  334. if not TryStrToDate(S,Result) then
  335. if not TryStrToTime(S,Result) then
  336. Raise EConvertError.CreateFmt(SInvalidDateTime,[S]);
  337. // ExtractDateTime(PropData.AsString)
  338. end;
  339. procedure TJSONDeStreamer.RestoreProperty(AObject : TObject;PropInfo : PPropInfo; PropData : TJSONData);
  340. Var
  341. B : Boolean;
  342. begin
  343. try
  344. B:=Not Assigned(FOnRestoreProp);
  345. If Not B then
  346. begin
  347. FOnRestoreProp(Self,AObject,PropInfo,PropData,B);
  348. If B then
  349. exit;
  350. end;
  351. DoRestoreProperty(AObject,PropInfo,PropData);
  352. except
  353. On E : Exception do
  354. If Assigned(FOnPropError) then
  355. begin
  356. B:=False;
  357. FOnPropError(Self,AObject,PropInfo,PropData,E,B);
  358. If Not B then
  359. Raise;
  360. end
  361. else if Not (jdoIgnorePropertyErrors in Options) then
  362. Raise;
  363. end;
  364. end;
  365. procedure TJSONDeStreamer.DoRestoreProperty(AObject : TObject;PropInfo : PPropInfo; PropData : TJSONData);
  366. Var
  367. PI : PPropInfo;
  368. TI : PTypeInfo;
  369. I,J,S : Integer;
  370. D : Double;
  371. A : TJSONArray;
  372. JS : TJSONStringType;
  373. begin
  374. PI:=PropInfo;
  375. TI:=PropInfo^.PropType;
  376. case TI^.Kind of
  377. tkUnknown :
  378. Error(SErrUnknownPropertyKind,[PI^.Name]);
  379. tkInteger :
  380. SetOrdProp(AObject,PI,PropData.AsInteger);
  381. tkInt64 :
  382. SetOrdProp(AObject,PI,PropData.AsInt64);
  383. tkEnumeration :
  384. begin
  385. if (PropData.JSONType=jtNumber) then
  386. I:=PropData.AsInteger
  387. else if PropData.JSONType=jtString then
  388. I:=GetEnumValue(TI,PropData.AsString)
  389. else
  390. Error(SErrUnSupportedEnumDataType,[PI^.Name,GetEnumName(TypeInfo(TJSONType),Ord(PropData.JSONType))]);
  391. SetOrdProp(AObject,PI,I);
  392. end;
  393. tkFloat :
  394. begin
  395. if (TI=TypeInfo(TDateTime)) and (PropData.JSONType=jtString) then
  396. SetFloatProp(AObject,PI,ExtractDateTime(PropData.AsString))
  397. else
  398. SetFloatProp(AObject,PI,PropData.AsFloat)
  399. end;
  400. tkSet :
  401. If PropData.JSONType=jtString then
  402. SetSetProp(AObject,PI,PropData.AsString)
  403. else if (PropData.JSONType=jtArray) then
  404. begin
  405. A:=PropData as TJSONArray;
  406. TI:=GetTypeData(TI)^.CompType;
  407. S:=0;
  408. For I:=0 to A.Count-1 do
  409. begin
  410. if A.types[i]=jtNumber then
  411. J:=A.Integers[i]
  412. else
  413. J:=GetEnumValue(TI,A.strings[i]);
  414. TSet(S):=TSet(S)+[j];
  415. end;
  416. SetOrdProp(AObject,PI,S);
  417. end;
  418. tkChar:
  419. begin
  420. JS:=PropData.AsString;
  421. If (JS<>'') then
  422. SetOrdProp(AObject,PI,Ord(JS[1]));
  423. end;
  424. tkSString,
  425. tkLString,
  426. tkAString:
  427. SetStrProp(AObject,PI,PropData.AsString);
  428. tkWString :
  429. SetWideStrProp(AObject,PI,PropData.AsUnicodeString);
  430. tkVariant:
  431. SetVariantProp(AObject,PI,JSONToVariant(PropData));
  432. tkClass:
  433. DeStreamClassProperty(AObject,PI,PropData);
  434. tkWChar :
  435. begin
  436. JS:=PropData.asString;
  437. If (JS<>'') then
  438. SetOrdProp(AObject,PI,Ord(JS[1]));
  439. end;
  440. tkBool :
  441. SetOrdProp(AObject,PI,Ord(PropData.AsBoolean));
  442. tkQWord :
  443. SetOrdProp(AObject,PI,Trunc(PropData.AsFloat));
  444. tkObject,
  445. tkArray,
  446. tkRecord,
  447. tkInterface,
  448. tkDynArray,
  449. tkInterfaceRaw,
  450. tkProcVar,
  451. tkMethod :
  452. Error(SErrUnsupportedPropertyKind,[PI^.Name]);
  453. tkUString :
  454. SetUnicodeStrProp(AObject,PI,PropData.AsUnicodeString);
  455. tkUChar:
  456. begin
  457. JS:=PropData.asString;
  458. If (JS<>'') then
  459. SetOrdProp(AObject,PI,Ord(JS[1]));
  460. end;
  461. end;
  462. end;
  463. function TJSONDeStreamer.DoMapProperty(AObject: TObject; PropInfo: PPropInfo; JSON: TJSONObject): TJSONData;
  464. var
  465. J: Integer;
  466. begin
  467. J := JSON.IndexOfName(PropInfo^.Name,(jdoCaseInsensitive in Options));
  468. if J > -1 then
  469. Result := JSON.Items[J]
  470. else
  471. Result := nil;
  472. end;
  473. procedure TJSONDeStreamer.DoBeforeReadObject(Const JSON: TJSONObject; AObject: TObject);
  474. begin
  475. If Assigned(FBeforeReadObject) then
  476. FBeforeReadObject(Self,AObject,JSON);
  477. end;
  478. procedure TJSONDeStreamer.DoAfterReadObject(Const JSON: TJSONObject; AObject: TObject);
  479. begin
  480. If Assigned(FAfterReadObject) then
  481. FAfterReadObject(Self,AObject,JSON)
  482. end;
  483. procedure TJSONDeStreamer.JSONToObject(const JSON: TJSONObject; AObject: TObject
  484. );
  485. Var
  486. I : Integer;
  487. PIL : TPropInfoList;
  488. JD: TJSONData;
  489. begin
  490. DoBeforeReadObject(JSON, AObject);
  491. If (AObject is TStrings) then
  492. JSONToStrings(JSON,AObject as TStrings)
  493. else If (AObject is TCollection) then
  494. JSONToCollection(JSON, AObject as TCollection)
  495. else
  496. begin
  497. Pil:=TPropInfoList.Create(AObject,tkProperties);
  498. try
  499. For I:=0 to PIL.Count-1 do
  500. begin
  501. JD:=DoMapProperty(AObject, Pil.Items[i], JSON);
  502. If Assigned(JD) then
  503. RestoreProperty(AObject,PIL.Items[i],JD);
  504. end;
  505. finally
  506. FreeAndNil(PIL);
  507. end;
  508. end;
  509. DoAfterReadObject(JSON, AObject);
  510. end;
  511. procedure TJSONDeStreamer.JSONToCollection(const JSON: TJSONStringType;
  512. ACollection: TCollection);
  513. Var
  514. D : TJSONData;
  515. begin
  516. D:=ObjectFromString(JSON);
  517. try
  518. if Assigned(D) then
  519. JSONToCollection(D,ACollection);
  520. finally
  521. D.Free;
  522. end;
  523. end;
  524. procedure TJSONDeStreamer.JSONToCollection(const JSON: TJSONData;
  525. ACollection: TCollection);
  526. Var
  527. I : integer;
  528. A : TJSONArray;
  529. O : TJSONObject;
  530. begin
  531. If (JSON.JSONType=jtArray) then
  532. A:=JSON As TJSONArray
  533. else if JSON.JSONType=jtObject then
  534. A:=(JSON as TJSONObject).Arrays['Items']
  535. else
  536. Error(SErrUnsupportedCollectionType,[JSONTypeName(JSON.JSONType)]);
  537. ACollection.Clear;
  538. For I:=0 to A.Count-1 do
  539. If (A.Types[i]<>jtObject) then
  540. Error(SErrUnsupportedCollectionItemType,[I,JSONTypeName(A.Types[I])])
  541. else
  542. JSONToObject(A.Objects[i],ACollection.Add);
  543. end;
  544. procedure TJSONDeStreamer.JSONToStrings(const JSON: TJSONStringType;
  545. AStrings: TSTrings);
  546. Var
  547. D : TJSONData;
  548. begin
  549. D:=ObjectFromString(JSON);
  550. try
  551. JSONToStrings(D,AStrings);
  552. finally
  553. D.Free;
  554. end;
  555. end;
  556. function TJSONDeStreamer.GetObject(AInstance: TObject;
  557. const APropName: TJSONStringType; D: TJSONObject; PropInfo: PPropInfo
  558. ): TObject;
  559. Var
  560. C : TClass;
  561. begin
  562. Result:=Nil;
  563. If Assigned(FOnGetObject) then
  564. FOnGetObject(Self,AInstance,PropInfo,D,APropName,Result);
  565. If (Result=Nil) and (AInstance is TComponent) and Assigned(PropInfo) then
  566. begin
  567. C:=GetTypeData(Propinfo^.PropType)^.ClassType;
  568. If C.InheritsFrom(TComponent) then
  569. Result:=TComponentClass(C).Create(TComponent(AInstance));
  570. end;
  571. end;
  572. procedure TJSONDeStreamer.JSONToStrings(const JSON: TJSONData;
  573. AStrings: TSTrings);
  574. Var
  575. O : TJSONObject;
  576. D : TJSONData;
  577. I : Integer;
  578. IO : TObject;
  579. N : TJSONStringType;
  580. begin
  581. Case JSON.JSONType of
  582. jtString:
  583. AStrings.Text:=JSON.AsString;
  584. jtArray:
  585. begin
  586. AStrings.Clear;
  587. For I:=0 to JSON.Count-1 do
  588. begin
  589. if not (JSON.Items[i].JSONType=jtString) then
  590. Error(SErrUnsupportedStringsItemType,[i,JSONTypeName(JSON.Items[i].JSONType)]);
  591. AStrings.Add(JSON.Items[i].AsString);
  592. end;
  593. end;
  594. jtObject:
  595. begin
  596. O:=JSON As TJSONObject;
  597. If (O.Count=1) and (O.Names[0]='Strings') and (O.Items[0].JSONType=jtArray) then
  598. JSONToStrings(O.Items[0],AStrings)
  599. else
  600. begin
  601. AStrings.Clear;
  602. For I:=0 to O.Count-1 do
  603. begin
  604. D:=O.Items[i];
  605. N:=O.Names[i];
  606. If D.JSONType=jtNull then
  607. IO:=Nil
  608. else if D.JSONType=jtObject then
  609. IO:=GetObject(AStrings,N,TJSONOBject(D),Nil)
  610. else
  611. Error(SErrUnsupportedStringsObjectType,[D,JSONTypeName(D.JSONType)]);
  612. AStrings.AddObject(O.Names[i],IO);
  613. end;
  614. end;
  615. end;
  616. else
  617. Error(SErrUnsupportedStringsType,[JSONTypeName(JSON.JSONType)]);
  618. end;
  619. end;
  620. Procedure TStreamChildrenHelper.StreamChild(AChild : TComponent);
  621. begin
  622. FChildren.Add(FStreamer.ObjectToJSON(AChild));
  623. end;
  624. Function TStreamChildrenHelper.StreamChildren(AComponent : TComponent; AStreamer : TJSONStreamer): TJSONArray;
  625. begin
  626. FStreamer:=AStreamer;
  627. Result:=TJSONArray.Create;
  628. try
  629. FChildren:=Result;
  630. THackComponent(AComponent).GetChildren(@StreamChild,AComponent);
  631. except
  632. FreeAndNil(Result);
  633. Raise;
  634. end;
  635. end;
  636. { TJSONFiler }
  637. procedure TJSONFiler.Error(Const Msg: String);
  638. begin
  639. Raise EJSONRTTI.Create(Name+' : '+Msg);
  640. end;
  641. procedure TJSONFiler.Error(Const FMT: String; Args: array of const);
  642. begin
  643. Raise EJSONRTTI.CreateFmt(Name+' : '+FMT,Args);
  644. end;
  645. { TJSONStreamer }
  646. constructor TJSONStreamer.Create(AOwner: TComponent);
  647. begin
  648. Inherited;
  649. end;
  650. destructor TJSONStreamer.Destroy;
  651. begin
  652. Inherited;
  653. end;
  654. Function TJSONStreamer.StreamChildren(AComp : TComponent) : TJSONArray;
  655. begin
  656. With TStreamChildrenHelper.Create do
  657. try
  658. Result:=StreamChildren(AComp,Self);
  659. finally
  660. Free;
  661. end;
  662. end;
  663. function TJSONStreamer.GetChildProperty: String;
  664. begin
  665. Result:=FChildProperty;
  666. If (Result='') then
  667. Result:='Children';
  668. end;
  669. function TJSONStreamer.IsChildStored: boolean;
  670. begin
  671. Result:=(GetChildProperty<>'Children');
  672. end;
  673. function TJSONStreamer.ObjectToJSON(Const AObject: TObject): TJSONObject;
  674. Var
  675. PIL : TPropInfoList;
  676. PD : TJSONData;
  677. I : Integer;
  678. begin
  679. Result:=Nil;
  680. If (AObject=Nil) then
  681. Exit;
  682. Result:=TJSONObject.Create;
  683. try
  684. If Assigned(FBeforeStreamObject) then
  685. FBeforeStreamObject(Self,AObject,Result);
  686. If AObject is TStrings then
  687. Result.Add('Strings',StreamTStrings(Tstrings(AObject)))
  688. else If AObject is TCollection then
  689. Result.Add('Items',StreamCollection(TCollection(AObject)))
  690. else If AObject is TObjectList then
  691. Result.Add('Objects',StreamObjectList(TObjectList(AObject)))
  692. else if (jsoStreamTlist in Options) and (AObject is TList) then
  693. Result.Add('Objects', StreamTList(TList(AObject)))
  694. else
  695. begin
  696. PIL:=TPropInfoList.Create(AObject,tkProperties);
  697. try
  698. For I:=0 to PIL.Count-1 do
  699. begin
  700. PD:=StreamProperty(AObject,PIL.Items[i]);
  701. If (PD<>Nil) then begin
  702. if jsoLowerPropertyNames in Options then
  703. Result.Add(LowerCase(PIL.Items[I]^.Name),PD)
  704. else
  705. Result.Add(PIL.Items[I]^.Name,PD);
  706. end;
  707. end;
  708. finally
  709. FReeAndNil(Pil);
  710. end;
  711. If (jsoStreamChildren in Options) and (AObject is TComponent) then
  712. Result.Add(ChildProperty,StreamChildren(TComponent(AObject)));
  713. If Assigned(FAfterStreamObject) then
  714. FAfterStreamObject(Self,AObject,Result);
  715. end;
  716. except
  717. FreeAndNil(Result);
  718. Raise;
  719. end;
  720. end;
  721. function TJSONStreamer.StreamProperty(Const AObject: TObject; Const PropertyName : String): TJSONData;
  722. begin
  723. Result:=StreamProperty(AObject,GetPropInfo(AObject,PropertyName));
  724. end;
  725. Function TJSONStreamer.StreamVariant(Const Data : Variant): TJSONData;
  726. Var
  727. A : TJSONArray;
  728. I : Integer;
  729. begin
  730. Result:=Nil;
  731. If VarIsArray(Data) then
  732. begin
  733. A:=TJSONArray.Create;
  734. try
  735. For I:=VarArrayLowBound(Data,1) to VarArrayHighBound(Data,1) do
  736. A.Add(StreamVariant(Data[i]));
  737. except
  738. FreeAndNil(A);
  739. Raise;
  740. end;
  741. Exit(A);
  742. end;
  743. If VarIsEmpty(Data) or VarisNull(Data) or (Data=UnAssigned) then
  744. Exit(TJSONNull.Create);
  745. Case VarType(Data) of
  746. varshortint,
  747. varbyte,
  748. varword,
  749. varsmallint,
  750. varinteger :
  751. Result:=TJSONIntegerNumber.Create(Data);
  752. varlongword,
  753. varint64 :
  754. Result:=TJSONInt64Number.Create(Data);
  755. vardecimal,
  756. varqword,
  757. varsingle,
  758. vardouble,
  759. varCurrency :
  760. Result:=TJSONFloatNumber.Create(Data);
  761. varString,
  762. varolestr :
  763. Result:=TJSONString.Create(Data);
  764. varboolean :
  765. Result:=TJSONBoolean.Create(Data);
  766. varDate :
  767. if jsoDateTimeAsString in Options then
  768. Result:=FormatDateProp(Data)
  769. else
  770. Result:=TJSONFloatNumber.Create(Data);
  771. else
  772. Error(SErrUnsupportedVariantType,[VarType(Data)])
  773. end;
  774. end;
  775. function TJSONStreamer.ObjectToJSONString(AObject: TObject): TJSONStringType;
  776. Var
  777. O : TJSONData;
  778. begin
  779. O:=ObjectToJSON(AObject);
  780. try
  781. if (jsoUseFormatString in Options) then
  782. Result:=O.FormatJSON()
  783. else
  784. Result:=O.AsJSON;
  785. finally
  786. FreeAndNil(O);
  787. end;
  788. end;
  789. function TJSONStreamer.StringsToJSON(Const Strings: TStrings; AsObject: Boolean = False): TJSONStringType;
  790. Var
  791. D : TJSONData;
  792. begin
  793. If ASObject then
  794. D:=StreamTSTringsObject(Strings)
  795. else
  796. D:=StreamTStringsArray(Strings);
  797. try
  798. if (jsoUseFormatString in Options) then
  799. Result:=D.FormatJSON
  800. else
  801. Result:=D.AsJSON;
  802. finally
  803. FreeAndNil(D);
  804. end;
  805. end;
  806. function TJSONStreamer.CollectionToJSON(const ACollection: TCollection
  807. ): TJSONStringType;
  808. Var
  809. D : TJSONArray;
  810. begin
  811. D:=StreamCollection(ACollection);
  812. try
  813. if (jsoUseFormatString in Options) then
  814. Result:=D.FormatJSON()
  815. else
  816. Result:=D.AsJSON;
  817. finally
  818. FreeAndNil(D);
  819. end;
  820. end;
  821. function TJSONStreamer.VariantToJSON(const Data: Variant): TJSONStringType;
  822. Var
  823. D : TJSONData;
  824. begin
  825. D:=StreamVariant(Data);
  826. try
  827. if (jsoUseFormatString in Options) then
  828. Result:=D.FormatJSON()
  829. else
  830. Result:=D.AsJSON;
  831. finally
  832. FreeAndNil(D);
  833. end;
  834. end;
  835. function TJSONStreamer.StreamTList(const AList: TList): TJSONArray;
  836. var
  837. I : Integer;
  838. o : TJSONObject;
  839. begin
  840. Result:=TJSONArray.Create;
  841. try
  842. for I:=0 to AList.Count-1 do begin
  843. o := ObjectToJSON(TObject(AList.Items[i]));
  844. if Assigned(o) then
  845. Result.Add(o);
  846. end;
  847. except
  848. FreeAndNil(Result);
  849. Raise;
  850. end;
  851. end;
  852. Function TJSONStreamer.StreamTStringsArray(Const AStrings : TStrings) : TJSONArray;
  853. Var
  854. I : Integer;
  855. begin
  856. Result:=TJSONArray.Create;
  857. try
  858. For I:=0 to AStrings.Count-1 do
  859. Result.Add(AStrings[i]);
  860. except
  861. FreeAndNil(Result);
  862. Raise;
  863. end;
  864. end;
  865. function TJSONStreamer.StreamTStringsObject(Const AStrings: TStrings): TJSONObject;
  866. Var
  867. I : Integer;
  868. O : TJSONData;
  869. begin
  870. Result:=TJSONObject.Create;
  871. try
  872. For I:=0 to AStrings.Count-1 do
  873. begin
  874. O:=ObjectToJSON(AStrings.Objects[i]);
  875. If O=Nil then
  876. O:=TJSONNull.Create;
  877. Result.Add(AStrings[i],O);
  878. end;
  879. except
  880. FreeAndNil(Result);
  881. Raise;
  882. end;
  883. end;
  884. function TJSONStreamer.StreamTStrings(Const AStrings: TStrings): TJSONData;
  885. begin
  886. If jsoTStringsAsArray in Options then
  887. Result:=StreamTStringsArray(AStrings)
  888. else If jsoTStringsAsObject in Options then
  889. Result:=StreamTStringsObject(AStrings)
  890. else
  891. Result:=TJSONString.Create(AStrings.Text);
  892. end;
  893. Function TJSONStreamer.StreamCollection(Const ACollection : TCollection) : TJSONArray;
  894. Var
  895. I : Integer;
  896. begin
  897. Result:=TJSONArray.Create;
  898. try
  899. For I:=0 to ACollection.Count-1 do
  900. Result.Add(ObjectToJSON(ACollection.Items[i]));
  901. except
  902. FreeAndNil(Result);
  903. Raise;
  904. end;
  905. end;
  906. function TJSONStreamer.StreamObjectList(const AnObjectList: TObjectList): TJSONArray;
  907. Var
  908. I : Integer;
  909. begin
  910. if not Assigned(AnObjectList) then
  911. Result:=Nil;
  912. Result:=TJSONArray.Create;
  913. try
  914. For I:=0 to AnObjectList.Count-1 do
  915. Result.Add(ObjectToJSON(AnObjectList.Items[i]));
  916. except
  917. FreeAndNil(Result);
  918. Raise;
  919. end;
  920. end;
  921. function TJSONStreamer.StreamClassProperty(const AObject: TObject): TJSONData;
  922. Var
  923. C : TCollection;
  924. I : integer;
  925. begin
  926. Result:=Nil;
  927. If (AObject=Nil) then
  928. Result:=TJSONNull.Create()
  929. else if (AObject is TComponent) then
  930. begin
  931. if (csSubComponent in TComponent(AObject).ComponentStyle) or (jsoComponentsInline in Options) then
  932. Result:=ObjectToJSON(AObject)
  933. else
  934. Result:=TJSONString.Create(TComponent(AObject).Name);
  935. end
  936. else if (AObject is TStrings) then
  937. Result:=StreamTStrings(TStrings(AObject))
  938. else if (AObject is TCollection) then
  939. Result:=StreamCollection(TCollection(Aobject))
  940. else If AObject is TObjectList then
  941. Result:=StreamObjectList(TObjectList(AObject))
  942. else // Normally, this is only TPersistent.
  943. Result:=ObjectToJSON(AObject);
  944. end;
  945. function TJSONStreamer.StreamProperty(Const AObject: TObject; PropertyInfo: PPropInfo): TJSONData;
  946. Var
  947. PI : PPropInfo;
  948. PT : PTypeInfo;
  949. S,I : integer;
  950. begin
  951. Result:=Nil;
  952. PI:=PropertyInfo;
  953. PT:=PI^.PropType;
  954. Case PT^.Kind of
  955. tkUnknown :
  956. Error(SErrUnknownPropertyKind,[PI^.Name]);
  957. tkInteger :
  958. Result:=TJSONIntegerNumber.Create(GetOrdProp(AObject,PI));
  959. tkEnumeration :
  960. if jsoEnumeratedAsInteger in Options then
  961. Result:=TJSONIntegerNumber.Create(GetOrdProp(AObject,PI))
  962. else
  963. Result:=TJSONString.Create(GetEnumName(PT,GetOrdProp(AObject,PI)));
  964. tkFloat :
  965. if (PT=TypeInfo(TDateTime)) and (jsoDateTimeAsString in Options) then
  966. Result:=FormatDateProp(GetFloatProp(AObject,PI))
  967. else
  968. Result:=TJSONFloatNumber.Create(GetFloatProp(AObject,PI));
  969. tkSet :
  970. If jsoSetAsString in Options then
  971. Result:=TJSONString.Create(GetSetProp(AObject,PI,jsoSetBrackets in Options))
  972. else
  973. begin
  974. PT:=GetTypeData(PT)^.CompType;
  975. S:=GetOrdProp(AObject,PI);
  976. Result:=TJSONArray.Create;
  977. try
  978. for i:=0 to 31 do
  979. if (i in TSet(S)) then
  980. if jsoSetEnumeratedAsInteger in Options then
  981. TJSONArray(Result).Add(i)
  982. else
  983. TJSONArray(Result).Add(GetEnumName(PT, i));
  984. except
  985. FreeAndNil(Result);
  986. Raise;
  987. end;
  988. end;
  989. tkChar:
  990. Result:=TJSONString.Create(Char(GetOrdProp(AObject,PI)));
  991. tkSString,
  992. tkLString,
  993. tkAString:
  994. Result:=TJSONString.Create(GetStrProp(AObject,PI));
  995. tkWString :
  996. Result:=TJSONString.Create(GetWideStrProp(AObject,PI));
  997. tkVariant:
  998. Result:=StreamVariant(GetVariantProp(AObject,PI));
  999. tkClass:
  1000. Result:=StreamClassProperty(GetObjectProp(AObject,PI));
  1001. tkWChar :
  1002. Result:=TJSONString.Create(WideChar(GetOrdProp(AObject,PI)));
  1003. tkBool :
  1004. Result:=TJSONBoolean.Create(GetOrdProp(AObject,PropertyInfo)<>0);
  1005. tkInt64 :
  1006. Result:=TJSONInt64Number.Create(GetOrdProp(AObject,PropertyInfo));
  1007. tkQWord :
  1008. Result:=TJSONFloatNumber.Create(GetOrdProp(AObject,PropertyInfo));
  1009. tkObject :
  1010. Result:=ObjectToJSON(GetObjectProp(AObject,PropertyInfo));
  1011. tkArray,
  1012. tkRecord,
  1013. tkInterface,
  1014. tkDynArray,
  1015. tkInterfaceRaw,
  1016. tkProcVar,
  1017. tkMethod :
  1018. Error(SErrUnsupportedPropertyKind,[PI^.Name]);
  1019. tkUString :
  1020. Result:=TJSONString.Create(GetWideStrProp(AObject,PI));
  1021. tkUChar:
  1022. Result:=TJSONString.Create(UnicodeChar(GetOrdProp(AObject,PI)));
  1023. end;
  1024. If Assigned(FOnStreamProperty) then
  1025. FOnStreamProperty(Self,AObject,PI,Result);
  1026. end;
  1027. function TJSONStreamer.FormatDateProp(Const DateTime: TDateTime): TJSONString;
  1028. Var
  1029. S: String;
  1030. begin
  1031. if (jsoCheckEmptyDateTime in Options) and (DateTime=0) then
  1032. S:=''
  1033. else if (DateTimeFormat<>'') then
  1034. S:=FormatDateTime(DateTimeFormat,DateTime)
  1035. else if (jsoLegacyDateTime in options) then
  1036. begin
  1037. if Frac(DateTime)=0 then
  1038. S:=DateToStr(DateTime)
  1039. else if Trunc(DateTime)=0 then
  1040. S:=TimeToStr(DateTime)
  1041. else
  1042. S:=DateTimeToStr(DateTime);
  1043. end
  1044. else
  1045. S:=FormatDateTime(RFC3339DateTimeFormat,DateTime);
  1046. Result:=TJSONString.Create(S);
  1047. end;
  1048. end.