UJSONFunctions.pas 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055
  1. unit UJSONFunctions;
  2. {$IFDEF FPC}
  3. {$MODE Delphi}
  4. {$ENDIF}
  5. { Copyright (c) 2016 by Albert Molina
  6. Distributed under the MIT software license, see the accompanying file LICENSE
  7. or visit http://www.opensource.org/licenses/mit-license.php.
  8. This unit is a part of Pascal Coin, a P2P crypto currency without need of
  9. historical operations.
  10. If you like it, consider a donation using BitCoin:
  11. 16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
  12. }
  13. interface
  14. Uses
  15. {$IFDEF FPC}
  16. fpjson, jsonparser,
  17. {$ELSE}
  18. DBXJSON,
  19. {$ENDIF}
  20. SysUtils, DateUtils, Variants, Classes, ULog;
  21. Type
  22. {$IFDEF FPC}
  23. TJSONValue = TJSONData;
  24. {$ENDIF}
  25. TPCJSONData = Class
  26. private
  27. FParent : TPCJSONData;
  28. protected
  29. Function ToJSONFormatted(pretty:Boolean;Const prefix : AnsiString) : AnsiString; virtual; abstract;
  30. public
  31. Constructor Create; virtual;
  32. Destructor Destroy; override;
  33. Class Function ParseJSONValue(Const JSONObject : String) : TPCJSONData; overload;
  34. Class Function ParseJSONValue(Const JSONObject : TBytes) : TPCJSONData; overload;
  35. Class Function _GetCount : Integer;
  36. Function ToJSON(pretty : Boolean) : AnsiString;
  37. Procedure SaveToStream(Stream : TStream);
  38. Procedure Assign(PCJSONData : TPCJSONData);
  39. End;
  40. TPCJSONDataClass = Class of TPCJSONData;
  41. { TPCJSONVariantValue }
  42. TPCJSONVariantValue = Class(TPCJSONData)
  43. private
  44. FOldValue : Variant;
  45. FWritable : Boolean;
  46. FValue: Variant;
  47. procedure SetValue(const Value: Variant);
  48. protected
  49. Function ToJSONFormatted(pretty:Boolean;const prefix : AnsiString) : AnsiString; override;
  50. public
  51. Constructor Create; override;
  52. Constructor CreateFromJSONValue(JSONValue : TJSONValue);
  53. Property Value : Variant read FValue write SetValue;
  54. Function AsString(DefValue : String) : String;
  55. Function AsInteger(DefValue : Integer) : Integer;
  56. Function AsInt64(DefValue : Int64) : Int64;
  57. Function AsDouble(DefValue : Double) : Double;
  58. Function AsBoolean(DefValue : Boolean) : Boolean;
  59. Function AsDateTime(DefValue : TDateTime) : TDateTime;
  60. Function AsCurrency(DefValue : Currency) : Currency;
  61. Function AsCardinal(DefValue : Cardinal) : Cardinal;
  62. Function IsNull : Boolean;
  63. End;
  64. TPCJSONNameValue = Class(TPCJSONData)
  65. private
  66. FName: String;
  67. FValue: TPCJSONData;
  68. FFreeValue : Boolean;
  69. procedure SetValue(const Value: TPCJSONData);
  70. protected
  71. Function ToJSONFormatted(pretty:Boolean;const prefix : AnsiString) : AnsiString; override;
  72. public
  73. Constructor Create(AName : String);
  74. Destructor Destroy; override;
  75. Property Name : String read FName;
  76. Property Value : TPCJSONData read FValue write SetValue;
  77. End;
  78. TPCJSONArray = class;
  79. TPCJSONObject = Class;
  80. TPCJSONList = Class(TPCJSONData)
  81. private
  82. FList : TList;
  83. function GetItems(Index: Integer): TPCJSONData;
  84. procedure SetItems(Index: Integer; const Value: TPCJSONData);
  85. protected
  86. Function GetIndexAsVariant(Index : Integer) : TPCJSONVariantValue;
  87. Function GetIndexAsArray(Index : Integer) : TPCJSONArray;
  88. Function GetIndexAsObject(Index : Integer) : TPCJSONObject;
  89. Procedure CheckCanInsert(Index:Integer; PCJSONData:TPCJSONData); virtual;
  90. public
  91. Constructor Create; override;
  92. Destructor Destroy; override;
  93. Property Items[Index:Integer] : TPCJSONData read GetItems write SetItems;
  94. Procedure Insert(Index:Integer; PCJSONData:TPCJSONData);
  95. Procedure Delete(index : Integer);
  96. function Count : Integer;
  97. Procedure Clear;
  98. End;
  99. TPCJSONArray = class(TPCJSONList)
  100. private
  101. Procedure GrowToIndex(index : Integer);
  102. function GetItemOfType(Index: Integer; DataClass:TPCJSONDataClass): TPCJSONData;
  103. protected
  104. Function ToJSONFormatted(pretty:Boolean;const prefix : AnsiString) : AnsiString; override;
  105. public
  106. Constructor Create; override;
  107. Constructor CreateFromJSONArray(JSONArray : TJSONArray);
  108. Destructor Destroy; override;
  109. Function GetAsVariant(index : Integer) : TPCJSONVariantValue;
  110. Function GetAsObject(index : Integer) : TPCJSONObject;
  111. Function GetAsArray(index : Integer) : TPCJSONArray;
  112. end;
  113. { TPCJSONObject }
  114. TPCJSONObject = Class(TPCJSONList)
  115. private
  116. Function GetIndexOrCreateName(Name : String) : Integer;
  117. Function GetByName(Name : String) : TPCJSONNameValue;
  118. protected
  119. Function ToJSONFormatted(pretty:Boolean;const prefix : AnsiString) : AnsiString; override;
  120. Procedure CheckCanInsert(Index:Integer; PCJSONData:TPCJSONData); override;
  121. Procedure CheckValidName(Name : String);
  122. public
  123. Constructor Create; override;
  124. Constructor CreateFromJSONObject(JSONObject : TJSONObject);
  125. Destructor Destroy; override;
  126. Function FindName(Name : String) : TPCJSONNameValue;
  127. Function IndexOfName(Name : String) : Integer;
  128. Procedure DeleteName(Name : String);
  129. Function GetAsVariant(Name : String) : TPCJSONVariantValue;
  130. Function GetAsObject(Name : String) : TPCJSONObject;
  131. Function GetAsArray(Name : String) : TPCJSONArray;
  132. Function AsString(ParamName : String; DefValue : String) : String;
  133. Function AsInteger(ParamName : String; DefValue : Integer) : Integer;
  134. Function AsCardinal(ParamName : String; DefValue : Cardinal) : Cardinal;
  135. Function AsInt64(ParamName : String; DefValue : Int64) : Int64;
  136. Function AsDouble(ParamName : String; DefValue : Double) : Double;
  137. Function AsBoolean(ParamName : String; DefValue : Boolean) : Boolean;
  138. Function AsDateTime(ParamName : String; DefValue : TDateTime) : TDateTime;
  139. Function AsCurrency(ParamName : String; DefValue : Currency) : Currency;
  140. Function SaveAsStream(ParamName : String; Stream : TStream) : Integer;
  141. Function LoadAsStream(ParamName : String; Stream : TStream) : Integer;
  142. Function GetNameValue(index : Integer) : TPCJSONNameValue;
  143. Function IsNull(ParamName : String) : Boolean;
  144. Procedure SetAs(Name : String; Value : TPCJSONData);
  145. End;
  146. EPCParametresError = Class(Exception);
  147. implementation
  148. Function UTF8JSONEncode(plainTxt : String; includeSeparator : Boolean) : String;
  149. Var ws : WideString;
  150. i : Integer;
  151. Begin
  152. ws := UTF8Encode(plainTxt);
  153. {ALERT:
  154. UTF8Encode function deletes last char if equal to #0, so we put it manually
  155. }
  156. if copy(plainTxt,length(plainTxt),1)=#0 then ws := ws + #0;
  157. i := 1;
  158. result := '"';
  159. while i <= length(ws) do
  160. begin
  161. case ws[i] of
  162. '/', '\', '"': result := result + '\' + ws[i];
  163. #8: result := result + '\b';
  164. #9: result := result + '\t';
  165. #10: result := result + '\n';
  166. #13: result := result + '\r';
  167. #12: result := result + '\f';
  168. else
  169. if (ord(ws[i]) < 32) Or (ord(ws[i])>122) then
  170. result := result + '\u' + inttohex(ord(ws[i]), 4)
  171. else
  172. result := result + ws[i];
  173. end;
  174. inc(i);
  175. end;
  176. result := result + '"';
  177. End;
  178. { TPCJSONArray }
  179. constructor TPCJSONArray.Create;
  180. begin
  181. inherited;
  182. end;
  183. constructor TPCJSONArray.CreateFromJSONArray(JSONArray: TJSONArray);
  184. Var i : Integer;
  185. begin
  186. Create;
  187. {$IFDEF FPC}
  188. for i := 0 to JSONArray.Count - 1 do begin
  189. if (JSONArray.Items[i] is TJSONArray) then begin
  190. Insert(i,TPCJSONArray.CreateFromJSONArray(TJSONArray(JSONArray.Items[i])));
  191. end else if (JSONArray.Items[i] is TJSONObject) then begin
  192. Insert(i,TPCJSONObject.CreateFromJSONObject(TJSONObject(JSONArray.Items[i])));
  193. end else if (JSONArray.Items[i] is TJSONValue) then begin
  194. Insert(i,TPCJSONVariantValue.CreateFromJSONValue(TJSONValue(JSONArray.Items[i])));
  195. end else raise EPCParametresError.Create('Invalid TJSON Data: '+JSONArray.Items[i].ClassName);
  196. end;
  197. {$ELSE}
  198. for i := 0 to JSONArray.Size - 1 do begin
  199. if (JSONArray.Get(i) is TJSONArray) then begin
  200. Insert(i,TPCJSONArray.CreateFromJSONArray(TJSONArray(JSONArray.Get(i))));
  201. end else if (JSONArray.Get(i) is TJSONObject) then begin
  202. Insert(i,TPCJSONObject.CreateFromJSONObject(TJSONObject(JSONArray.Get(i))));
  203. end else if (JSONArray.Get(i) is TJSONValue) then begin
  204. Insert(i,TPCJSONVariantValue.CreateFromJSONValue(TJSONValue(JSONArray.Get(i))));
  205. end else raise EPCParametresError.Create('Invalid TJSON Data: '+JSONArray.Get(i).ClassName);
  206. end;
  207. {$ENDIF}
  208. end;
  209. destructor TPCJSONArray.Destroy;
  210. begin
  211. inherited;
  212. end;
  213. function TPCJSONArray.GetAsArray(index: Integer): TPCJSONArray;
  214. begin
  215. Result := GetItemOfType(index,TPCJSONArray) as TPCJSONArray;
  216. end;
  217. function TPCJSONArray.GetAsObject(index: Integer): TPCJSONObject;
  218. begin
  219. Result := GetItemOfType(index,TPCJSONObject) as TPCJSONObject;
  220. end;
  221. function TPCJSONArray.GetAsVariant(index: Integer): TPCJSONVariantValue;
  222. begin
  223. Result := GetItemOfType(index,TPCJSONVariantValue) as TPCJSONVariantValue;
  224. end;
  225. function TPCJSONArray.GetItemOfType(Index: Integer;
  226. DataClass: TPCJSONDataClass): TPCJSONData;
  227. Var V,New : TPCJSONData;
  228. begin
  229. GrowToIndex(Index);
  230. V := GetItems(index);
  231. if Not (V is DataClass) then begin
  232. New := DataClass.Create;
  233. Items[index] := New;
  234. V := New;
  235. end;
  236. Result := V as DataClass;
  237. end;
  238. procedure TPCJSONArray.GrowToIndex(index: Integer);
  239. begin
  240. While (index>=Count) do Insert(Count,TPCJSONVariantValue.Create);
  241. end;
  242. function TPCJSONArray.ToJSONFormatted(pretty: Boolean; const prefix: AnsiString): AnsiString;
  243. Var i : Integer;
  244. begin
  245. If pretty then Result := prefix+'['
  246. else Result := '[';
  247. for i := 0 to Count - 1 do begin
  248. if (i>0) then begin
  249. Result := Result+',';
  250. If pretty then Result :=Result +#10+prefix;
  251. end;
  252. Result := Result + Items[i].ToJSONFormatted(pretty,prefix+' ');
  253. end;
  254. Result := Result+']';
  255. end;
  256. { TPCJSONList }
  257. procedure TPCJSONList.CheckCanInsert(Index: Integer; PCJSONData: TPCJSONData);
  258. begin
  259. if (Index<0) Or (Index>Count) then raise Exception.Create('Invalid insert at index '+Inttostr(Index)+' (Count:'+Inttostr(Count)+')');
  260. end;
  261. procedure TPCJSONList.Clear;
  262. begin
  263. while (FList.Count>0) do Delete(FList.Count-1);
  264. end;
  265. function TPCJSONList.Count: Integer;
  266. begin
  267. Result := FList.Count;
  268. end;
  269. constructor TPCJSONList.Create;
  270. begin
  271. inherited;
  272. FParent := Nil;
  273. FList := TList.Create;
  274. end;
  275. procedure TPCJSONList.Delete(index: Integer);
  276. Var M : TPCJSONData;
  277. begin
  278. M := GetItems(index);
  279. FList.Delete(index);
  280. M.Free;
  281. end;
  282. destructor TPCJSONList.Destroy;
  283. begin
  284. Clear;
  285. FList.Free;
  286. inherited;
  287. end;
  288. function TPCJSONList.GetIndexAsArray(Index: Integer): TPCJSONArray;
  289. Var D : TPCJSONData;
  290. begin
  291. D := GetItems(Index);
  292. if (Not (D is TPCJSONArray)) then begin
  293. Result := TPCJSONArray.Create;
  294. SetItems(Index,Result);
  295. D.Free;
  296. end else Result := TPCJSONArray(D);
  297. end;
  298. function TPCJSONList.GetIndexAsObject(Index: Integer): TPCJSONObject;
  299. Var D : TPCJSONData;
  300. begin
  301. D := GetItems(Index);
  302. if (Not (D is TPCJSONObject)) then begin
  303. Result := TPCJSONObject.Create;
  304. SetItems(Index,Result);
  305. D.Free;
  306. end else Result := TPCJSONObject(D);
  307. end;
  308. function TPCJSONList.GetIndexAsVariant(Index: Integer): TPCJSONVariantValue;
  309. Var D : TPCJSONData;
  310. begin
  311. D := GetItems(Index);
  312. if (Not (D is TPCJSONVariantValue)) then begin
  313. Result := TPCJSONVariantValue.Create;
  314. SetItems(Index,Result);
  315. D.Free;
  316. end else Result := TPCJSONVariantValue(D);
  317. end;
  318. function TPCJSONList.GetItems(Index: Integer): TPCJSONData;
  319. begin
  320. Result := FList.Items[Index];
  321. end;
  322. procedure TPCJSONList.Insert(Index: Integer; PCJSONData: TPCJSONData);
  323. begin
  324. CheckCanInsert(Index,PCJSONData);
  325. FList.Insert(Index,PCJSONData);
  326. end;
  327. procedure TPCJSONList.SetItems(Index: Integer; const Value: TPCJSONData);
  328. Var OldP : TPCJSONData;
  329. begin
  330. OldP := FList.Items[Index];
  331. Try
  332. FList.Items[Index] := Value;
  333. Finally
  334. OldP.Free;
  335. End;
  336. end;
  337. { TPCJSONVariantValue }
  338. Function VariantToDouble(Value : Variant) : Double;
  339. Var s : String;
  340. Begin
  341. Result := 0;
  342. Case varType(Value) of
  343. varSmallint, varInteger, varSingle, varDouble,
  344. varCurrency : Result := Value;
  345. Else
  346. Begin
  347. s := VarToStr(Value);
  348. If s='' Then Abort
  349. Else Result := StrToFloat(s);
  350. End;
  351. End;
  352. End;
  353. function TPCJSONVariantValue.AsBoolean(DefValue: Boolean): Boolean;
  354. begin
  355. try
  356. Result := VarAsType(Value,varBoolean);
  357. except
  358. Result := DefValue;
  359. end;
  360. end;
  361. function TPCJSONVariantValue.AsCurrency(DefValue: Currency): Currency;
  362. begin
  363. try
  364. Result := VariantToDouble(Value);
  365. except
  366. Result := DefValue;
  367. end;
  368. end;
  369. function TPCJSONVariantValue.AsCardinal(DefValue: Cardinal): Cardinal;
  370. begin
  371. Result := Cardinal( StrToIntDef(VarToStrDef(Value,''),DefValue) );
  372. end;
  373. function TPCJSONVariantValue.AsDateTime(DefValue: TDateTime): TDateTime;
  374. begin
  375. try
  376. Result := VarAsType(Value,varDate);
  377. except
  378. Result := DefValue;
  379. end;
  380. end;
  381. function TPCJSONVariantValue.AsDouble(DefValue: Double): Double;
  382. begin
  383. try
  384. Result := VariantToDouble(Value);
  385. except
  386. Result := DefValue;
  387. end;
  388. end;
  389. function TPCJSONVariantValue.AsInt64(DefValue: Int64): Int64;
  390. begin
  391. Result := StrToInt64Def(VarToStrDef(Value,''),DefValue);
  392. end;
  393. function TPCJSONVariantValue.AsInteger(DefValue: Integer): Integer;
  394. begin
  395. Result := StrToIntDef(VarToStrDef(Value,''),DefValue);
  396. end;
  397. function TPCJSONVariantValue.AsString(DefValue: String): String;
  398. begin
  399. try
  400. Case VarType(Value) of
  401. varNull : Result := '';
  402. varSmallint, varInteger :
  403. Begin
  404. Result := inttostr(Value);
  405. End;
  406. varSingle, varDouble,varCurrency :
  407. Begin
  408. Result := FloatToStr(VariantToDouble(Value));
  409. End;
  410. varDate : Result := DateTimeToStr(Value);
  411. Else Result := VarToStr(Value);
  412. End;
  413. except
  414. Result := DefValue;
  415. end;
  416. end;
  417. constructor TPCJSONVariantValue.Create;
  418. begin
  419. inherited;
  420. FValue := Null;
  421. FOldValue := Unassigned;
  422. FWritable := False;
  423. end;
  424. constructor TPCJSONVariantValue.CreateFromJSONValue(JSONValue: TJSONValue);
  425. {$IFnDEF FPC}
  426. Var d : Double;
  427. i64 : Integer;
  428. ds,ts : Char;
  429. {$ENDIF}
  430. begin
  431. Create;
  432. {$IFDEF FPC}
  433. Value := JSONValue.Value;
  434. {$ELSE}
  435. if JSONValue is TJSONNumber then begin
  436. d := TJSONNumber(JSONValue).AsDouble;
  437. if Pos('.',JSONValue.ToString)>0 then i64 := 0
  438. else i64 := TJSONNumber(JSONValue).AsInt;
  439. ds := DecimalSeparator;
  440. ts := ThousandSeparator;
  441. DecimalSeparator := '.';
  442. ThousandSeparator := ',';
  443. Try
  444. if FormatFloat('0.###########',d)=inttostr(i64) then
  445. Value := i64
  446. else Value := d;
  447. Finally
  448. DecimalSeparator := ds;
  449. ThousandSeparator := ts;
  450. End;
  451. end else if JSONValue is TJSONTrue then Value := true
  452. else if JSONValue is TJSONFalse then Value := false
  453. else if JSONValue is TJSONNull then Value := Null
  454. else Value := JSONValue.Value;
  455. {$ENDIF}
  456. end;
  457. function TPCJSONVariantValue.IsNull: Boolean;
  458. begin
  459. Result := VarIsNull(FValue) or VarIsEmpty(FValue);
  460. end;
  461. procedure TPCJSONVariantValue.SetValue(const Value: Variant);
  462. begin
  463. FOldValue := FValue;
  464. FValue := Value;
  465. end;
  466. function TPCJSONVariantValue.ToJSONFormatted(pretty: Boolean; const prefix: AnsiString): AnsiString;
  467. Var ds,ts : Char;
  468. begin
  469. Case VarType(Value) of
  470. varSmallint,varInteger,varByte,varWord,
  471. varLongWord,varInt64 : Result := VarToStr(Value);
  472. varBoolean : if (Value) then Result := 'true' else Result:='false';
  473. varNull : Result := 'null';
  474. varDate,varDouble : begin
  475. ds := DecimalSeparator;
  476. ts := ThousandSeparator;
  477. DecimalSeparator := '.';
  478. ThousandSeparator := ',';
  479. try
  480. Result := FormatFloat('0.###########',Value);
  481. finally
  482. DecimalSeparator := ds;
  483. ThousandSeparator := ts;
  484. end;
  485. end
  486. else
  487. Result := UTF8JSONEncode(VarToStr(Value),true);
  488. end;
  489. end;
  490. { TPCJSONObject }
  491. function TPCJSONObject.AsBoolean(ParamName: String; DefValue: Boolean): Boolean;
  492. Var v : Variant;
  493. VV : TPCJSONVariantValue;
  494. begin
  495. VV := GetAsVariant(ParamName);
  496. if (VarType(VV.Value)=varNull) AND (VarType( VV.FOldValue ) = varEmpty) then begin
  497. Result := DefValue;
  498. Exit;
  499. end;
  500. v := GetAsVariant(ParamName).Value;
  501. try
  502. if VarIsNull(v) then Result := DefValue
  503. else Result := VarAsType(v,varBoolean);
  504. except
  505. Result := DefValue;
  506. end;
  507. end;
  508. function TPCJSONObject.AsCardinal(ParamName: String; DefValue: Cardinal): Cardinal;
  509. begin
  510. Result := Cardinal(AsInt64(ParamName,DefValue));
  511. end;
  512. function TPCJSONObject.AsCurrency(ParamName: String; DefValue: Currency): Currency;
  513. Var v : Variant;
  514. VV : TPCJSONVariantValue;
  515. begin
  516. VV := GetAsVariant(ParamName);
  517. if (VarType(VV.Value)=varNull) AND (VarType( VV.FOldValue ) = varEmpty) then begin
  518. Result := DefValue;
  519. Exit;
  520. end;
  521. v := GetAsVariant(ParamName).Value;
  522. try
  523. if VarIsNull(v) then Result := DefValue
  524. else Result := VariantToDouble(v);
  525. except
  526. Result := DefValue;
  527. end;
  528. end;
  529. function TPCJSONObject.AsDateTime(ParamName: String;
  530. DefValue: TDateTime): TDateTime;
  531. Var v : Variant;
  532. VV : TPCJSONVariantValue;
  533. begin
  534. VV := GetAsVariant(ParamName);
  535. if (VarType(VV.Value)=varNull) AND (VarType( VV.FOldValue ) = varEmpty) then begin
  536. Result := DefValue;
  537. Exit;
  538. end;
  539. v := GetAsVariant(ParamName).Value;
  540. try
  541. if VarIsNull(v) then Result := DefValue
  542. else Result := VarAsType(v,varDate);
  543. except
  544. Result := DefValue;
  545. end;
  546. end;
  547. function TPCJSONObject.AsDouble(ParamName: String; DefValue: Double): Double;
  548. Var v : Variant;
  549. VV : TPCJSONVariantValue;
  550. begin
  551. VV := GetAsVariant(ParamName);
  552. if (VarType(VV.Value)=varNull) AND (VarType( VV.FOldValue ) = varEmpty) then begin
  553. Result := DefValue;
  554. Exit;
  555. end;
  556. v := GetAsVariant(ParamName).Value;
  557. try
  558. if VarIsNull(v) then Result := DefValue
  559. else Result := VariantToDouble(v);
  560. except
  561. Result := DefValue;
  562. end;
  563. end;
  564. function TPCJSONObject.AsInt64(ParamName: String; DefValue: Int64): Int64;
  565. Var v : Variant;
  566. VV : TPCJSONVariantValue;
  567. begin
  568. VV := GetAsVariant(ParamName);
  569. if (VarType(VV.Value)=varNull) AND (VarType( VV.FOldValue ) = varEmpty) then begin
  570. Result := DefValue;
  571. Exit;
  572. end;
  573. v := GetAsVariant(ParamName).Value;
  574. try
  575. if VarIsNull(v) then Result := DefValue
  576. else Result := StrToInt64Def(VarToStrDef(v,''),DefValue);
  577. except
  578. Result := DefValue;
  579. end;
  580. end;
  581. function TPCJSONObject.AsInteger(ParamName: String; DefValue: Integer): Integer;
  582. Var v : Variant;
  583. VV : TPCJSONVariantValue;
  584. begin
  585. VV := GetAsVariant(ParamName);
  586. if (VarType(VV.Value)=varNull) AND (VarType( VV.FOldValue ) = varEmpty) then begin
  587. Result := DefValue;
  588. Exit;
  589. end;
  590. v := GetAsVariant(ParamName).Value;
  591. try
  592. if VarIsNull(v) then Result := DefValue
  593. else Result := StrToIntDef(VarToStrDef(v,''),DefValue);
  594. except
  595. Result := DefValue;
  596. end;
  597. end;
  598. function TPCJSONObject.AsString(ParamName: String; DefValue: String): String;
  599. Var v : Variant;
  600. VV : TPCJSONVariantValue;
  601. begin
  602. VV := GetAsVariant(ParamName);
  603. if (VarType(VV.Value)=varNull) AND (VarType( VV.FOldValue ) = varEmpty) then begin
  604. Result := DefValue;
  605. Exit;
  606. end;
  607. v := GetAsVariant(ParamName).Value;
  608. try
  609. Case VarType(V) of
  610. varNull : Result := '';
  611. varSmallint, varInteger :
  612. Begin
  613. Result := inttostr(v);
  614. End;
  615. varSingle, varDouble,varCurrency :
  616. Begin
  617. Result := FloatToStr(VariantToDouble(v));
  618. End;
  619. varDate : Result := DateTimeToStr(v);
  620. Else Result := VarToStr(v);
  621. End;
  622. except
  623. Result := DefValue;
  624. end;
  625. end;
  626. procedure TPCJSONObject.CheckCanInsert(Index: Integer; PCJSONData: TPCJSONData);
  627. begin
  628. inherited;
  629. if Not Assigned(PCJSONData) then raise Exception.Create('Object is nil');
  630. if Not (PCJSONData is TPCJSONNameValue) then raise Exception.Create('Object inside a '+TPCJSONData.ClassName+' must be a '+TPCJSONNameValue.ClassName+' (currently '+PCJSONData.ClassName+')');
  631. end;
  632. procedure TPCJSONObject.CheckValidName(Name: String);
  633. Var i : Integer;
  634. begin
  635. for i := 1 to Length(Name) do begin
  636. if i=1 then begin
  637. if Not (Name[i] in ['a'..'z','A'..'Z','0'..'9','_','.']) then raise Exception.Create(Format('Invalid char %s at pos %d/%d',[Name[i],i,length(Name)]));
  638. end else begin
  639. if Not (Name[i] in ['a'..'z','A'..'Z','0'..'9','_','-','.']) then raise Exception.Create(Format('Invalid char %s at pos %d/%d',[Name[i],i,length(Name)]));
  640. end;
  641. end;
  642. end;
  643. constructor TPCJSONObject.Create;
  644. begin
  645. inherited;
  646. end;
  647. constructor TPCJSONObject.CreateFromJSONObject(JSONObject: TJSONObject);
  648. var i,i2 : Integer;
  649. {$IFDEF FPC}
  650. aname : TJSONStringType;
  651. {$ENDIF}
  652. begin
  653. Create;
  654. {$IFDEF FPC}
  655. for i := 0 to JSONObject.Count - 1 do begin
  656. aname := JSONObject.Names[i];
  657. i2 := GetIndexOrCreateName(JSONObject.Names[i]);
  658. if (JSONObject.Types[ aname ] = jtArray) then begin
  659. (Items[i2] as TPCJSONNameValue).Value := TPCJSONArray.CreateFromJSONArray(JSONObject.Arrays[aname]);
  660. end else if (JSONObject.Types[ aname ] = jtObject) then begin
  661. (Items[i2] as TPCJSONNameValue).Value := TPCJSONObject.CreateFromJSONObject(JSONObject.Objects[aname]);
  662. end else if (JSONObject.Types[ aname ] in [jtBoolean,jtNull,jtNumber,jtString]) then begin
  663. (Items[i2] as TPCJSONNameValue).Value := TPCJSONVariantValue.CreateFromJSONValue(JSONObject.Items[i]);
  664. end else raise EPCParametresError.Create('Invalid TJSON Data in JSONObject.'+aname+': '+JSONObject.Items[i].ClassName);
  665. end;
  666. {$ELSE}
  667. for i := 0 to JSONObject.Size - 1 do begin
  668. i2 := GetIndexOrCreateName(JSONObject.Get(i).JsonString.Value);
  669. if (JSONObject.Get(i).JsonValue is TJSONArray) then begin
  670. (Items[i2] as TPCJSONNameValue).Value := TPCJSONArray.CreateFromJSONArray(TJSONArray(JSONObject.Get(i).JsonValue));
  671. end else if (JSONObject.Get(i).JsonValue is TJSONObject) then begin
  672. (Items[i2] as TPCJSONNameValue).Value := TPCJSONObject.CreateFromJSONObject(TJSONObject(JSONObject.Get(i).JsonValue));
  673. end else if (JSONObject.Get(i).JsonValue is TJSONValue) then begin
  674. (Items[i2] as TPCJSONNameValue).Value := TPCJSONVariantValue.CreateFromJSONValue(TJSONValue(JSONObject.Get(i).JsonValue));
  675. end else raise EPCParametresError.Create('Invalid TJSON Data in JSONObject.'+JSONObject.Get(i).JsonString.Value+': '+JSONObject.Get(i).ClassName);
  676. end;
  677. {$ENDIF}
  678. end;
  679. procedure TPCJSONObject.DeleteName(Name: String);
  680. Var i : Integer;
  681. begin
  682. i := IndexOfName(Name);
  683. if (i>=0) then begin
  684. Delete(i);
  685. end;
  686. end;
  687. destructor TPCJSONObject.Destroy;
  688. begin
  689. inherited;
  690. end;
  691. function TPCJSONObject.FindName(Name: String): TPCJSONNameValue;
  692. Var i : Integer;
  693. begin
  694. i := IndexOfName(Name);
  695. Result := Nil;
  696. if (i>=0) then Result := Items[i] as TPCJSONNameValue;
  697. end;
  698. function TPCJSONObject.GetAsArray(Name: String): TPCJSONArray;
  699. Var NV : TPCJSONNameValue;
  700. V : TPCJSONData;
  701. begin
  702. NV := GetByName(Name);
  703. if Not (NV.Value is TPCJSONArray) then begin
  704. NV.Value := TPCJSONArray.Create;
  705. end;
  706. Result := NV.Value as TPCJSONArray;
  707. end;
  708. function TPCJSONObject.GetAsObject(Name: String): TPCJSONObject;
  709. Var NV : TPCJSONNameValue;
  710. V : TPCJSONData;
  711. begin
  712. NV := GetByName(Name);
  713. if Not (NV.Value is TPCJSONObject) then begin
  714. NV.Value := TPCJSONObject.Create;
  715. end;
  716. Result := NV.Value as TPCJSONObject;
  717. end;
  718. function TPCJSONObject.GetAsVariant(Name: String): TPCJSONVariantValue;
  719. Var NV : TPCJSONNameValue;
  720. V : TPCJSONData;
  721. begin
  722. NV := GetByName(Name);
  723. if Not (NV.Value is TPCJSONVariantValue) then begin
  724. NV.Value := TPCJSONVariantValue.Create;
  725. end;
  726. Result := NV.Value as TPCJSONVariantValue;
  727. end;
  728. function TPCJSONObject.GetByName(Name: String): TPCJSONNameValue;
  729. Var i : Integer;
  730. begin
  731. i := GetIndexOrCreateName(Name);
  732. Result := Items[i] as TPCJSONNameValue;
  733. end;
  734. function TPCJSONObject.GetIndexOrCreateName(Name: String): Integer;
  735. Var
  736. NV : TPCJSONNameValue;
  737. Begin
  738. Result := IndexOfName(Name);
  739. if (Result<0) then begin
  740. CheckValidName(Name);
  741. NV := TPCJSONNameValue.Create(Name);
  742. Result := FList.Add(NV);
  743. end;
  744. end;
  745. function TPCJSONObject.GetNameValue(index: Integer): TPCJSONNameValue;
  746. begin
  747. Result := Items[index] as TPCJSONNameValue;
  748. end;
  749. function TPCJSONObject.IsNull(ParamName: String): Boolean;
  750. Var i : Integer;
  751. NV : TPCJSONNameValue;
  752. begin
  753. i := IndexOfName(ParamName);
  754. if i<0 then result := true
  755. else begin
  756. Result := false;
  757. NV := TPCJSONNameValue( FList.Items[i] );
  758. If (Assigned(NV.Value)) AND (NV.Value is TPCJSONVariantValue) then begin
  759. Result := TPCJSONVariantValue(NV.Value).IsNull;
  760. end;
  761. end;
  762. end;
  763. function TPCJSONObject.IndexOfName(Name: String): Integer;
  764. begin
  765. for Result := 0 to FList.Count - 1 do begin
  766. if (Assigned(FList.Items[Result])) And (TObject(FList.Items[Result]) is TPCJSONNameValue) then begin
  767. If TPCJSONNameValue( FList.Items[Result] ).Name = Name then begin
  768. exit;
  769. end;
  770. end;
  771. end;
  772. Result := -1;
  773. end;
  774. function TPCJSONObject.LoadAsStream(ParamName: String; Stream: TStream): Integer;
  775. Var s : AnsiString;
  776. begin
  777. s := AsString(ParamName,'');
  778. if (s<>'') then begin
  779. Stream.Write(s[1],length(s));
  780. end;
  781. Result := Length(s);
  782. end;
  783. function TPCJSONObject.SaveAsStream(ParamName: String; Stream: TStream): Integer;
  784. Var s : AnsiString;
  785. begin
  786. Stream.Position := 0;
  787. SetLength(s,Stream.Size);
  788. Stream.Read(s[1],Stream.Size);
  789. GetAsVariant(ParamName).Value := s;
  790. end;
  791. procedure TPCJSONObject.SetAs(Name: String; Value: TPCJSONData);
  792. // When assigning a object with SetAs this will not be freed automatically
  793. Var NV : TPCJSONNameValue;
  794. V : TPCJSONData;
  795. i : Integer;
  796. begin
  797. i := GetIndexOrCreateName(Name);
  798. NV := Items[i] as TPCJSONNameValue;
  799. NV.Value := Value;
  800. NV.FFreeValue := false;
  801. end;
  802. function TPCJSONObject.ToJSONFormatted(pretty: Boolean; const prefix: AnsiString): AnsiString;
  803. Var i : Integer;
  804. begin
  805. if pretty then Result := prefix+'{'
  806. else Result := '{';
  807. for i := 0 to Count - 1 do begin
  808. if (i>0) then Begin
  809. Result := Result+',';
  810. If pretty then Result :=Result +#10+prefix;
  811. End;
  812. Result := Result + Items[i].ToJSONFormatted(pretty,prefix+' ');
  813. end;
  814. Result := Result+'}';
  815. end;
  816. { TPCJSONNameValue }
  817. constructor TPCJSONNameValue.Create(AName: String);
  818. begin
  819. inherited Create;
  820. FName := AName;
  821. FValue := TPCJSONData.Create;
  822. FFreeValue := True;
  823. end;
  824. destructor TPCJSONNameValue.Destroy;
  825. begin
  826. if FFreeValue then FValue.Free;
  827. inherited;
  828. end;
  829. procedure TPCJSONNameValue.SetValue(const Value: TPCJSONData);
  830. Var old : TPCJSONData;
  831. begin
  832. if FValue=Value then exit;
  833. old := FValue;
  834. FValue := Value;
  835. if FFreeValue then old.Free;
  836. FFreeValue := true;
  837. end;
  838. function TPCJSONNameValue.ToJSONFormatted(pretty: Boolean; const prefix: AnsiString): AnsiString;
  839. begin
  840. if pretty then Result := prefix else Result := '';
  841. Result := Result + UTF8JSONEncode(name,true)+':'+Value.ToJSONFormatted(pretty,prefix+' ');
  842. end;
  843. { TPCJSONData }
  844. Var _objectsCount : Integer;
  845. procedure TPCJSONData.Assign(PCJSONData: TPCJSONData);
  846. Var i : Integer;
  847. NV : TPCJSONNameValue;
  848. JSOND : TPCJSONData;
  849. s : AnsiString;
  850. begin
  851. if Not Assigned(PCJSONData) then Abort;
  852. if (PCJSONData is TPCJSONObject) AND (Self is TPCJSONObject) then begin
  853. for i := 0 to TPCJSONObject(PCJSONData).Count - 1 do begin
  854. NV := TPCJSONObject(PCJSONData).Items[i] as TPCJSONNameValue;
  855. if NV.Value is TPCJSONObject then begin
  856. TPCJSONObject(Self).GetAsObject(NV.Name).Assign(NV.Value);
  857. end else if NV.Value is TPCJSONArray then begin
  858. TPCJSONObject(Self).GetAsArray(NV.Name).Assign(NV.Value);
  859. end else if NV.Value is TPCJSONVariantValue then begin
  860. TPCJSONObject(Self).GetAsVariant(NV.Name).Assign(NV.Value);
  861. end else raise Exception.Create('Error in TPCJSONData.Assign decoding '+NV.Name+' ('+NV.Value.ClassName+')');
  862. end;
  863. end else if (PCJSONData is TPCJSONArray) AND (Self is TPCJSONArray) then begin
  864. for i := 0 to TPCJSONArray(PCJSONData).Count - 1 do begin
  865. JSOND := TPCJSONArray(PCJSONData).Items[i];
  866. s := JSOND.ToJSON(false);
  867. TPCJSONArray(Self).Insert(TPCJSONArray(Self).Count,TPCJSONData.ParseJSONValue(s));
  868. end;
  869. end else if (PCJSONData is TPCJSONVariantValue) AND (Self is TPCJSONVariantValue) then begin
  870. TPCJSONVariantValue(Self).Value := TPCJSONVariantValue(PCJSONData).Value;
  871. end else begin
  872. raise Exception.Create('Error in TPCJSONData.Assign assigning a '+PCJSONData.ClassName+' to a '+ClassName);
  873. end;
  874. end;
  875. constructor TPCJSONData.Create;
  876. begin
  877. inc(_objectsCount);
  878. end;
  879. destructor TPCJSONData.Destroy;
  880. begin
  881. dec(_objectsCount);
  882. inherited;
  883. end;
  884. class function TPCJSONData.ParseJSONValue(Const JSONObject: TBytes): TPCJSONData;
  885. Var JS : TJSONValue;
  886. {$IFDEF FPC}
  887. jss : TJSONStringType;
  888. i : Integer;
  889. {$ENDIF}
  890. begin
  891. Result := Nil;
  892. JS := Nil;
  893. {$IFDEF FPC}
  894. SetLength(jss,length(JSONObject));
  895. for i:=0 to High(JSONObject) do jss[i+1] := AnsiChar( JSONObject[i] );
  896. Try
  897. JS := GetJSON(jss);
  898. Except
  899. On E:Exception do begin
  900. TLog.NewLog(ltDebug,ClassName,'Error processing JSON: '+E.Message);
  901. end;
  902. end;
  903. {$ELSE}
  904. Try
  905. JS := TJSONObject.ParseJSONValue(JSONObject,0);
  906. Except
  907. On E:Exception do begin
  908. TLog.NewLog(ltDebug,ClassName,'Error processing JSON: '+E.Message);
  909. end;
  910. End;
  911. {$ENDIF}
  912. if Not Assigned(JS) then exit;
  913. Try
  914. if JS is TJSONObject then begin
  915. Result := TPCJSONObject.CreateFromJSONObject(TJSONObject(JS));
  916. end else if JS is TJSONArray then begin
  917. Result := TPCJSONArray.CreateFromJSONArray(TJSONArray(JS));
  918. end else if JS is TJSONValue then begin
  919. Result := TPCJSONVariantValue.CreateFromJSONValue(TJSONValue(JS));
  920. end else raise EPCParametresError.Create('Invalid TJSON Data type '+JS.ClassName);
  921. Finally
  922. JS.Free;
  923. End;
  924. end;
  925. procedure TPCJSONData.SaveToStream(Stream: TStream);
  926. Var s : AnsiString;
  927. begin
  928. s := ToJSON(false);
  929. Stream.Write(s[1],length(s));
  930. end;
  931. class function TPCJSONData.ParseJSONValue(Const JSONObject: String): TPCJSONData;
  932. begin
  933. Result := ParseJSONValue( TEncoding.ASCII.GetBytes(JSONObject) );
  934. end;
  935. function TPCJSONData.ToJSON(pretty: Boolean): AnsiString;
  936. begin
  937. Result := ToJSONFormatted(pretty,'');
  938. end;
  939. class function TPCJSONData._GetCount: Integer;
  940. begin
  941. Result := _objectsCount;
  942. end;
  943. initialization
  944. _objectsCount := 0;
  945. end.