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