jsonini.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. unit jsonini;
  2. {$mode objfpc}
  3. {$h+}
  4. interface
  5. uses
  6. Classes, SysUtils, inifiles, fpjson, jsonscanner, jsonparser, dateutils;
  7. type
  8. { TJSONIniFile }
  9. TJSONIniFile = class(TCustomIniFile)
  10. Private
  11. FJSON: TJSONObject;
  12. FCacheUpdates: Boolean;
  13. FDirty : Boolean;
  14. FStream: TStream;
  15. procedure SetCacheUpdates(const AValue: Boolean);
  16. protected
  17. Function GetRoot : TJSONObject;
  18. Function GetSection(Const ASectionName : String; AllowCreate : Boolean) : TJSONObject;
  19. Function GetKeyData(Const ASectionName,AKeyName : String) : TJSONData;
  20. // Return true if an existing item was replaced
  21. Function SetKeyData(Const ASectionName,AKeyName : String; AData : TJSONData) : Boolean;
  22. procedure MaybeUpdateFile;
  23. property Dirty : Boolean Read FDirty;
  24. public
  25. constructor Create(const AFileName: string; AOptions : TIniFileOptions = []); override; overload;
  26. constructor Create(AStream: TStream; AOptions : TJSONOptions); overload;
  27. destructor Destroy; override;
  28. Class Procedure ConvertIni(Const AIniFile,AJSONFile : String; StringsOnly : Boolean = True);
  29. function ReadString(const Section, Ident, Default: string): string; override;
  30. function ReadInteger(const Section, Ident: string; Default: Longint): Longint; override;
  31. function ReadInt64(const Section, Ident: string; Default: Int64): Int64; override;
  32. function ReadBool(const Section, Ident: string; Default: Boolean): Boolean; override;
  33. function ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime; override;
  34. function ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime; override;
  35. function ReadFloat(const Section, Ident: string; Default: Double): Double; override;
  36. function ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime; override;
  37. procedure WriteString(const Section, Ident, Value: String); override;
  38. procedure WriteDate(const Section, Ident: string; Value: TDateTime); override;
  39. procedure WriteDateTime(const Section, Ident: string; Value: TDateTime); override;
  40. procedure WriteFloat(const Section, Ident: string; Value: Double); override;
  41. procedure WriteTime(const Section, Ident: string; Value: TDateTime); override;
  42. procedure WriteInteger(const Section, Ident: string; Value: Longint); override;
  43. procedure WriteInt64(const Section, Ident: string; Value: Int64); override;
  44. procedure WriteBool(const Section, Ident: string; Value: Boolean); override;
  45. procedure ReadSection(const Section: string; Strings: TStrings); override;
  46. procedure ReadSections(Strings: TStrings); override;
  47. procedure ReadSectionValues(const Section: string; Strings: TStrings; AOptions : TSectionValuesOptions = [svoIncludeInvalid]); overload; override;
  48. procedure EraseSection(const Section: string); override;
  49. procedure DeleteKey(const Section, Ident: String); override;
  50. procedure UpdateFile; override; overload;
  51. procedure UpdateFile(Const AFileName : string); overload;
  52. property Stream: TStream read FStream;
  53. property CacheUpdates : Boolean read FCacheUpdates write SetCacheUpdates;
  54. end;
  55. implementation
  56. { TJSONIniFile }
  57. procedure TJSONIniFile.SetCacheUpdates(const AValue: Boolean);
  58. begin
  59. if FCacheUpdates and not AValue and FDirty then
  60. UpdateFile;
  61. end;
  62. function TJSONIniFile.GetRoot: TJSONObject;
  63. begin
  64. Result:=FJSON;
  65. end;
  66. function TJSONIniFile.GetSection(const ASectionName: String; AllowCreate: Boolean): TJSONObject;
  67. Var
  68. I : Integer;
  69. R : TJSONObject;
  70. begin
  71. Result:=Nil;
  72. R:=GetRoot;
  73. I:=R.IndexOfName(ASectionName,True);
  74. if (I<>-1) and (R.Items[i].JSONType=jtObject) then
  75. Result:=R.Items[i] as TJSONObject
  76. else if AllowCreate then
  77. begin
  78. if (I<>-1) then
  79. R.Delete(I);
  80. Result:=TJSONObject.Create;
  81. R.Add(ASectionName,Result);
  82. end;
  83. end;
  84. function TJSONIniFile.GetKeyData(const ASectionName, AKeyName: String): TJSONData;
  85. Var
  86. O : TJSONObject;
  87. I : integer;
  88. begin
  89. Result:=Nil;
  90. O:=GetSection(ASectionName,False);
  91. if Assigned(O) then
  92. begin
  93. I:=O.IndexOfName(AKeyName,True);
  94. if (I<>-1) and (O.Items[i].JSONType in ActualValueJSONTypes) then
  95. Result:=O.Items[i];
  96. end
  97. end;
  98. function TJSONIniFile.SetKeyData(const ASectionName, AKeyName: String; AData: TJSONData): Boolean;
  99. Var
  100. O : TJSONObject;
  101. I : integer;
  102. begin
  103. O:=GetSection(ASectionName,true);
  104. I:=O.IndexOfName(AKeyName,True);
  105. Result:=(I<>-1);
  106. if Result then
  107. O.Delete(I);
  108. O.Add(aKeyName,AData);
  109. FDirty:=True;
  110. end;
  111. procedure TJSONIniFile.MaybeUpdateFile;
  112. begin
  113. If FCacheUpdates then
  114. FDirty:=True
  115. else
  116. UpdateFile;
  117. end;
  118. constructor TJSONIniFile.Create(const AFileName: string; AOptions : TIniFileOptions = []);
  119. Var
  120. F : TFileStream;
  121. begin
  122. Inherited Create(AFileName,AOptions);
  123. if Not FileExists(AFileName) then
  124. FJSON:=TJSONObject.Create
  125. else
  126. begin
  127. F:=TFileStream.Create(AFileName,fmOpenRead or fmShareDenyWrite);
  128. try
  129. Create(F,[joUTF8,joComments,joIgnoreTrailingComma]);
  130. finally
  131. F.Free;
  132. end;
  133. end;
  134. end;
  135. constructor TJSONIniFile.Create(AStream: TStream; AOptions: TJSONOptions);
  136. Var
  137. P : TJSONParser;
  138. D : TJSONData;
  139. begin
  140. D:=Nil;
  141. P:=TJSONParser.Create(AStream,AOptions);
  142. try
  143. D:=P.Parse;
  144. if (D is TJSONObject) then
  145. begin
  146. FJSON:=D as TJSONObject;
  147. D:=Nil;
  148. end
  149. else
  150. FJSON:=TJSONObject.Create;
  151. finally
  152. D.Free;
  153. P.Free;
  154. end;
  155. end;
  156. destructor TJSONIniFile.Destroy;
  157. begin
  158. FreeAndNil(FJSON);
  159. inherited Destroy;
  160. end;
  161. class procedure TJSONIniFile.ConvertIni(const AIniFile, AJSONFile: String; StringsOnly: Boolean = true);
  162. Var
  163. SIni : TMemIniFile;
  164. Dini : TJSONIniFile;
  165. S,K : TStrings;
  166. SN,KN,V : String;
  167. I6 : Int64;
  168. F : Double;
  169. B : Boolean;
  170. DT : TDateTime;
  171. begin
  172. S:=Nil;
  173. K:=Nil;
  174. Dini:=Nil;
  175. SIni:=TMemIniFile.Create(AIniFile);
  176. try
  177. DIni:=Self.Create(AJSONFile);
  178. S:=TStringList.Create;
  179. K:=TStringList.Create;
  180. SIni.ReadSections(S);
  181. For SN in S do
  182. begin
  183. SIni.ReadSection(SN,K);
  184. For KN in K do
  185. begin
  186. V:=Sini.ReadString(SN,KN,'');
  187. if StringsOnly then
  188. Dini.WriteString(SN,KN,V)
  189. else
  190. begin
  191. If TryStrToInt64(V,I6) then
  192. Dini.WriteInt64(SN,KN,I6)
  193. else If TryStrToFloat(V,F) then
  194. Dini.WriteFloat(SN,KN,F)
  195. else If TryStrToBool(V,B) then
  196. Dini.WriteBool(SN,KN,B)
  197. else
  198. begin
  199. DT:=SIni.ReadTime(SN,KN,-1);
  200. B:=DT<>-1;
  201. if B then
  202. DIni.WriteTime(SN,KN,DT)
  203. else
  204. begin
  205. DT:=SIni.ReadDate(SN,KN,0);
  206. B:=DT<>0;
  207. if B then
  208. DIni.WriteDate(SN,KN,DT)
  209. else
  210. begin
  211. DT:=SIni.ReadDateTime(SN,KN,0);
  212. B:=DT<>0;
  213. if B then
  214. DIni.WriteDateTime(SN,KN,DT)
  215. end;
  216. end;
  217. if Not B then
  218. Dini.WriteString(SN,KN,V)
  219. end;
  220. end;
  221. end;
  222. end;
  223. Dini.UpdateFile;
  224. finally
  225. FreeAndNil(S);
  226. FreeAndNil(K);
  227. FreeAndNil(Dini);
  228. FreeAndNil(Sini);
  229. end;
  230. end;
  231. function TJSONIniFile.ReadString(const Section, Ident, Default: string): string;
  232. Var
  233. D : TJSONData;
  234. begin
  235. D:=GetKeyData(Section,Ident);
  236. if Not Assigned(D) then
  237. Result:=Default
  238. else
  239. begin
  240. if D.JSONType in StructuredJSONTypes then
  241. Result:=D.AsJSON
  242. else
  243. Result:=D.AsString;
  244. end
  245. end;
  246. function TJSONIniFile.ReadInteger(const Section, Ident: string; Default: Longint): Longint;
  247. Var
  248. D : TJSONData;
  249. begin
  250. D:=GetKeyData(Section,Ident);
  251. if Not Assigned(D) then
  252. Result:=Default
  253. else
  254. if D.JSONType=jtNumber then
  255. Result:=D.AsInteger
  256. else
  257. if not TryStrToInt(D.AsString,Result) then
  258. Result:=Default;
  259. end;
  260. function TJSONIniFile.ReadInt64(const Section, Ident: string; Default: Int64): Int64;
  261. Var
  262. D : TJSONData;
  263. begin
  264. D:=GetKeyData(Section,Ident);
  265. if Not Assigned(D) then
  266. Result:=Default
  267. else
  268. if D.JSONType=jtNumber then
  269. Result:=D.AsInt64
  270. else
  271. if not TryStrToInt64(D.AsString,Result) then
  272. Result:=Default;
  273. end;
  274. function TJSONIniFile.ReadBool(const Section, Ident: string; Default: Boolean): Boolean;
  275. Var
  276. D : TJSONData;
  277. begin
  278. D:=GetKeyData(Section,Ident);
  279. if Not Assigned(D) then
  280. Result:=Default
  281. else
  282. // Avoid exception frame
  283. if D.JSONType=jtBoolean then
  284. Result:=D.AsBoolean
  285. else
  286. try
  287. Result:=D.AsBoolean;
  288. except
  289. Result:=Default;
  290. end;
  291. end;
  292. function TJSONIniFile.ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime;
  293. Var
  294. D : TJSONData;
  295. begin
  296. D:=GetKeyData(Section,Ident);
  297. if Not Assigned(D) then
  298. Result:=Default
  299. else if D.JSONType=jtNumber then
  300. Result:=TDateTime(D.AsFloat)
  301. else
  302. Result:=ScanDateTime('yyyy"-"mm"-"dd',D.AsString);
  303. end;
  304. function TJSONIniFile.ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime;
  305. Var
  306. D : TJSONData;
  307. begin
  308. D:=GetKeyData(Section,Ident);
  309. if Not Assigned(D) then
  310. Result:=Default
  311. else if D.JSONType=jtNumber then
  312. Result:=TDateTime(D.AsFloat)
  313. else
  314. Result:=ScanDateTime('yyyy"-"mm"-"dd"T"hh":"nn":"ss"."zzz',D.AsString);
  315. end;
  316. function TJSONIniFile.ReadFloat(const Section, Ident: string; Default: Double): Double;
  317. Var
  318. D : TJSONData;
  319. C : Integer;
  320. begin
  321. D:=GetKeyData(Section,Ident);
  322. if Not Assigned(D) then
  323. Result:=Default
  324. else
  325. if D.JSONType=jtNumber then
  326. Result:=D.AsFloat
  327. else
  328. // Localized
  329. if not TryStrToFloat(D.AsString,Result) then
  330. begin
  331. // Not localized
  332. Val(D.AsString,Result,C);
  333. if (C<>0) then
  334. Result:=Default;
  335. end;
  336. end;
  337. function TJSONIniFile.ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime;
  338. Var
  339. D : TJSONData;
  340. begin
  341. D:=GetKeyData(Section,Ident);
  342. if Not Assigned(D) then
  343. Result:=Default
  344. else if D.JSONType=jtNumber then
  345. Result:=Frac(TDateTime(D.AsFloat))
  346. else
  347. Result:=ScanDateTime('"0000-00-00T"hh":"nn":"ss"."zzz',D.AsString);
  348. end;
  349. procedure TJSONIniFile.WriteString(const Section, Ident, Value: String);
  350. begin
  351. SetKeyData(Section,Ident,CreateJSON(Value));
  352. end;
  353. procedure TJSONIniFile.WriteDate(const Section, Ident: string; Value: TDateTime);
  354. begin
  355. SetKeyData(Section,Ident,CreateJSON(FormatDateTime('yyyy"-"mm"-"dd"T"00":"00":"00.zzz',Value)));
  356. end;
  357. procedure TJSONIniFile.WriteDateTime(const Section, Ident: string; Value: TDateTime);
  358. begin
  359. SetKeyData(Section,Ident,CreateJSON(FormatDateTime('yyyy"-"mm"-"dd"T"hh":"nn":"ss.zzz',Value)));
  360. end;
  361. procedure TJSONIniFile.WriteFloat(const Section, Ident: string; Value: Double);
  362. begin
  363. SetKeyData(Section,Ident,CreateJSON(Value));
  364. end;
  365. procedure TJSONIniFile.WriteTime(const Section, Ident: string; Value: TDateTime);
  366. begin
  367. SetKeyData(Section,Ident,CreateJSON(FormatDateTime('0000"-"00"-"00"T"hh":"nn":"ss.zzz',Value)));
  368. end;
  369. procedure TJSONIniFile.WriteInteger(const Section, Ident: string; Value: Longint);
  370. begin
  371. SetKeyData(Section,Ident,CreateJSON(Value));
  372. end;
  373. procedure TJSONIniFile.WriteInt64(const Section, Ident: string; Value: Int64);
  374. begin
  375. SetKeyData(Section,Ident,CreateJSON(Value));
  376. end;
  377. procedure TJSONIniFile.WriteBool(const Section, Ident: string; Value: Boolean);
  378. begin
  379. SetKeyData(Section,Ident,CreateJSON(Value));
  380. end;
  381. procedure TJSONIniFile.ReadSection(const Section: string; Strings: TStrings);
  382. Var
  383. O : TJSONObject;
  384. E : TJSONEnum;
  385. begin
  386. O:=GetSection(Section,False);
  387. if Assigned(O) then
  388. For E in O do
  389. If (E.Value.JSONType in ActualValueJSONTypes) then
  390. Strings.Add(E.Key);
  391. end;
  392. procedure TJSONIniFile.ReadSections(Strings: TStrings);
  393. Var
  394. R : TJSONObject;
  395. E : TJSONEnum;
  396. begin
  397. R:=GetRoot;
  398. for E in R do
  399. if E.Value.JSONType=jtObject then
  400. Strings.Add(E.Key);
  401. end;
  402. procedure TJSONIniFile.ReadSectionValues(const Section: string; Strings: TStrings; AOptions: TSectionValuesOptions);
  403. Var
  404. O : TJSONObject;
  405. E : TJSONEnum;
  406. V : TJSONStringType;
  407. begin
  408. O:=GetSection(Section,False);
  409. if Assigned(O) then
  410. For E in O do
  411. begin
  412. If (E.Value.JSONType in ActualValueJSONTypes) then
  413. begin
  414. V:=E.Value.AsString;
  415. Strings.Add(E.Key+'='+V);
  416. end
  417. else if (svoIncludeInvalid in AOptions) then
  418. begin
  419. V:=E.Value.AsJSON;
  420. Strings.Add(E.Key+'='+V);
  421. end
  422. end;
  423. end;
  424. procedure TJSONIniFile.EraseSection(const Section: string);
  425. Var
  426. I : Integer;
  427. begin
  428. I:=GetRoot.IndexOfName(Section,True);
  429. if (I<>-1) then
  430. begin
  431. GetRoot.Delete(I);
  432. MaybeUpdateFile;
  433. end;
  434. end;
  435. procedure TJSONIniFile.DeleteKey(const Section, Ident: String);
  436. Var
  437. O : TJSONObject;
  438. I : integer;
  439. begin
  440. O:=GetSection(Section,False);
  441. if O<>Nil then
  442. begin
  443. I:=O.IndexOfName(Ident,True);
  444. if I<>-1 then
  445. begin
  446. O.Delete(I);
  447. MaybeUpdateFile;
  448. end;
  449. end;
  450. end;
  451. procedure TJSONIniFile.UpdateFile;
  452. begin
  453. If (FileName<>'') then
  454. UpdateFile(FileName)
  455. end;
  456. procedure TJSONIniFile.UpdateFile(const AFileName: string);
  457. Var
  458. S : TJSONStringType;
  459. begin
  460. With TFileStream.Create(AFileName,fmCreate) do
  461. try
  462. S:=FJSON.FormatJSON();
  463. WriteBuffer(S[1],Length(S));
  464. finally
  465. Free;
  466. end;
  467. end;
  468. end.