fpjsonrtti.pp 30 KB

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