123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- unit jsonini;
- {$mode objfpc}
- {$h+}
- interface
- uses
- Classes, SysUtils, inifiles, fpjson, jsonscanner, jsonparser, dateutils;
- type
- { TJSONIniFile }
- TJSONIniFile = class(TCustomIniFile)
- Private
- FJSON: TJSONObject;
- FCacheUpdates: Boolean;
- FDirty : Boolean;
- FStream: TStream;
- procedure SetCacheUpdates(const AValue: Boolean);
- protected
- Function GetRoot : TJSONObject;
- Function GetSection(Const ASectionName : String; AllowCreate : Boolean) : TJSONObject;
- Function GetKeyData(Const ASectionName,AKeyName : String) : TJSONData;
- // Return true if an existing item was replaced
- Function SetKeyData(Const ASectionName,AKeyName : String; AData : TJSONData) : Boolean;
- procedure MaybeUpdateFile;
- property Dirty : Boolean Read FDirty;
- public
- constructor Create(const AFileName: string; AOptions : TIniFileOptions = []); override; overload;
- constructor Create(AStream: TStream; AOptions : TJSONOptions); overload;
- destructor Destroy; override;
- Class Procedure ConvertIni(Const AIniFile,AJSONFile : String; StringsOnly : Boolean = True);
- function ReadString(const Section, Ident, Default: string): string; override;
- function ReadInteger(const Section, Ident: string; Default: Longint): Longint; override;
- function ReadInt64(const Section, Ident: string; Default: Int64): Int64; override;
- function ReadBool(const Section, Ident: string; Default: Boolean): Boolean; override;
- function ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime; override;
- function ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime; override;
- function ReadFloat(const Section, Ident: string; Default: Double): Double; override;
- function ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime; override;
- procedure WriteString(const Section, Ident, Value: String); override;
- procedure WriteDate(const Section, Ident: string; Value: TDateTime); override;
- procedure WriteDateTime(const Section, Ident: string; Value: TDateTime); override;
- procedure WriteFloat(const Section, Ident: string; Value: Double); override;
- procedure WriteTime(const Section, Ident: string; Value: TDateTime); override;
- procedure WriteInteger(const Section, Ident: string; Value: Longint); override;
- procedure WriteInt64(const Section, Ident: string; Value: Int64); override;
- procedure WriteBool(const Section, Ident: string; Value: Boolean); override;
- procedure ReadSection(const Section: string; Strings: TStrings); override;
- procedure ReadSections(Strings: TStrings); override;
- procedure ReadSectionValues(const Section: string; Strings: TStrings; AOptions : TSectionValuesOptions = [svoIncludeInvalid]); overload; override;
- procedure EraseSection(const Section: string); override;
- procedure DeleteKey(const Section, Ident: String); override;
- procedure UpdateFile; override; overload;
- procedure UpdateFile(Const AFileName : string); overload;
- property Stream: TStream read FStream;
- property CacheUpdates : Boolean read FCacheUpdates write SetCacheUpdates;
- end;
- implementation
- { TJSONIniFile }
- procedure TJSONIniFile.SetCacheUpdates(const AValue: Boolean);
- begin
- if FCacheUpdates and not AValue and FDirty then
- UpdateFile;
- end;
- function TJSONIniFile.GetRoot: TJSONObject;
- begin
- Result:=FJSON;
- end;
- function TJSONIniFile.GetSection(const ASectionName: String; AllowCreate: Boolean): TJSONObject;
- Var
- I : Integer;
- R : TJSONObject;
- begin
- Result:=Nil;
- R:=GetRoot;
- I:=R.IndexOfName(ASectionName,True);
- if (I<>-1) and (R.Items[i].JSONType=jtObject) then
- Result:=R.Items[i] as TJSONObject
- else if AllowCreate then
- begin
- if (I<>-1) then
- R.Delete(I);
- Result:=TJSONObject.Create;
- R.Add(ASectionName,Result);
- end;
- end;
- function TJSONIniFile.GetKeyData(const ASectionName, AKeyName: String): TJSONData;
- Var
- O : TJSONObject;
- I : integer;
- begin
- Result:=Nil;
- O:=GetSection(ASectionName,False);
- if Assigned(O) then
- begin
- I:=O.IndexOfName(AKeyName,True);
- if (I<>-1) and (O.Items[i].JSONType in ActualValueJSONTypes) then
- Result:=O.Items[i];
- end
- end;
- function TJSONIniFile.SetKeyData(const ASectionName, AKeyName: String; AData: TJSONData): Boolean;
- Var
- O : TJSONObject;
- I : integer;
- begin
- O:=GetSection(ASectionName,true);
- I:=O.IndexOfName(AKeyName,True);
- Result:=(I<>-1);
- if Result then
- O.Delete(I);
- O.Add(aKeyName,AData);
- FDirty:=True;
- end;
- procedure TJSONIniFile.MaybeUpdateFile;
- begin
- If FCacheUpdates then
- FDirty:=True
- else
- UpdateFile;
- end;
- constructor TJSONIniFile.Create(const AFileName: string; AOptions : TIniFileOptions = []);
- Var
- F : TFileStream;
- begin
- Inherited Create(AFileName,AOptions);
- if Not FileExists(AFileName) then
- FJSON:=TJSONObject.Create
- else
- begin
- F:=TFileStream.Create(AFileName,fmOpenRead or fmShareDenyWrite);
- try
- Create(F,[joUTF8,joComments,joIgnoreTrailingComma]);
- finally
- F.Free;
- end;
- end;
- end;
- constructor TJSONIniFile.Create(AStream: TStream; AOptions: TJSONOptions);
- Var
- P : TJSONParser;
- D : TJSONData;
- begin
- D:=Nil;
- P:=TJSONParser.Create(AStream,AOptions);
- try
- D:=P.Parse;
- if (D is TJSONObject) then
- begin
- FJSON:=D as TJSONObject;
- D:=Nil;
- end
- else
- FJSON:=TJSONObject.Create;
- finally
- D.Free;
- P.Free;
- end;
- end;
- destructor TJSONIniFile.Destroy;
- begin
- FreeAndNil(FJSON);
- inherited Destroy;
- end;
- class procedure TJSONIniFile.ConvertIni(const AIniFile, AJSONFile: String; StringsOnly: Boolean = true);
- Var
- SIni : TMemIniFile;
- Dini : TJSONIniFile;
- S,K : TStrings;
- SN,KN,V : String;
- I6 : Int64;
- F : Double;
- B : Boolean;
- DT : TDateTime;
- begin
- S:=Nil;
- K:=Nil;
- Dini:=Nil;
- SIni:=TMemIniFile.Create(AIniFile);
- try
- DIni:=Self.Create(AJSONFile);
- S:=TStringList.Create;
- K:=TStringList.Create;
- SIni.ReadSections(S);
- For SN in S do
- begin
- SIni.ReadSection(SN,K);
- For KN in K do
- begin
- V:=Sini.ReadString(SN,KN,'');
- if StringsOnly then
- Dini.WriteString(SN,KN,V)
- else
- begin
- If TryStrToInt64(V,I6) then
- Dini.WriteInt64(SN,KN,I6)
- else If TryStrToFloat(V,F) then
- Dini.WriteFloat(SN,KN,F)
- else If TryStrToBool(V,B) then
- Dini.WriteBool(SN,KN,B)
- else
- begin
- DT:=SIni.ReadTime(SN,KN,-1);
- B:=DT<>-1;
- if B then
- DIni.WriteTime(SN,KN,DT)
- else
- begin
- DT:=SIni.ReadDate(SN,KN,0);
- B:=DT<>0;
- if B then
- DIni.WriteDate(SN,KN,DT)
- else
- begin
- DT:=SIni.ReadDateTime(SN,KN,0);
- B:=DT<>0;
- if B then
- DIni.WriteDateTime(SN,KN,DT)
- end;
- end;
- if Not B then
- Dini.WriteString(SN,KN,V)
- end;
- end;
- end;
- end;
- Dini.UpdateFile;
- finally
- FreeAndNil(S);
- FreeAndNil(K);
- FreeAndNil(Dini);
- FreeAndNil(Sini);
- end;
- end;
- function TJSONIniFile.ReadString(const Section, Ident, Default: string): string;
- Var
- D : TJSONData;
- begin
- D:=GetKeyData(Section,Ident);
- if Not Assigned(D) then
- Result:=Default
- else
- begin
- if D.JSONType in StructuredJSONTypes then
- Result:=D.AsJSON
- else
- Result:=D.AsString;
- end
- end;
- function TJSONIniFile.ReadInteger(const Section, Ident: string; Default: Longint): Longint;
- Var
- D : TJSONData;
- begin
- D:=GetKeyData(Section,Ident);
- if Not Assigned(D) then
- Result:=Default
- else
- if D.JSONType=jtNumber then
- Result:=D.AsInteger
- else
- if not TryStrToInt(D.AsString,Result) then
- Result:=Default;
- end;
- function TJSONIniFile.ReadInt64(const Section, Ident: string; Default: Int64): Int64;
- Var
- D : TJSONData;
- begin
- D:=GetKeyData(Section,Ident);
- if Not Assigned(D) then
- Result:=Default
- else
- if D.JSONType=jtNumber then
- Result:=D.AsInt64
- else
- if not TryStrToInt64(D.AsString,Result) then
- Result:=Default;
- end;
- function TJSONIniFile.ReadBool(const Section, Ident: string; Default: Boolean): Boolean;
- Var
- D : TJSONData;
- begin
- D:=GetKeyData(Section,Ident);
- if Not Assigned(D) then
- Result:=Default
- else
- // Avoid exception frame
- if D.JSONType=jtBoolean then
- Result:=D.AsBoolean
- else
- try
- Result:=D.AsBoolean;
- except
- Result:=Default;
- end;
- end;
- function TJSONIniFile.ReadDate(const Section, Ident: string; Default: TDateTime): TDateTime;
- Var
- D : TJSONData;
- begin
- D:=GetKeyData(Section,Ident);
- if Not Assigned(D) then
- Result:=Default
- else if D.JSONType=jtNumber then
- Result:=TDateTime(D.AsFloat)
- else
- Result:=ScanDateTime('yyyy"-"mm"-"dd',D.AsString);
- end;
- function TJSONIniFile.ReadDateTime(const Section, Ident: string; Default: TDateTime): TDateTime;
- Var
- D : TJSONData;
- begin
- D:=GetKeyData(Section,Ident);
- if Not Assigned(D) then
- Result:=Default
- else if D.JSONType=jtNumber then
- Result:=TDateTime(D.AsFloat)
- else
- Result:=ScanDateTime('yyyy"-"mm"-"dd"T"hh":"nn":"ss"."zzz',D.AsString);
- end;
- function TJSONIniFile.ReadFloat(const Section, Ident: string; Default: Double): Double;
- Var
- D : TJSONData;
- C : Integer;
- begin
- D:=GetKeyData(Section,Ident);
- if Not Assigned(D) then
- Result:=Default
- else
- if D.JSONType=jtNumber then
- Result:=D.AsFloat
- else
- // Localized
- if not TryStrToFloat(D.AsString,Result) then
- begin
- // Not localized
- Val(D.AsString,Result,C);
- if (C<>0) then
- Result:=Default;
- end;
- end;
- function TJSONIniFile.ReadTime(const Section, Ident: string; Default: TDateTime): TDateTime;
- Var
- D : TJSONData;
- begin
- D:=GetKeyData(Section,Ident);
- if Not Assigned(D) then
- Result:=Default
- else if D.JSONType=jtNumber then
- Result:=Frac(TDateTime(D.AsFloat))
- else
- Result:=ScanDateTime('"0000-00-00T"hh":"nn":"ss"."zzz',D.AsString);
- end;
- procedure TJSONIniFile.WriteString(const Section, Ident, Value: String);
- begin
- SetKeyData(Section,Ident,CreateJSON(Value));
- end;
- procedure TJSONIniFile.WriteDate(const Section, Ident: string; Value: TDateTime);
- begin
- SetKeyData(Section,Ident,CreateJSON(FormatDateTime('yyyy"-"mm"-"dd"T"00":"00":"00.zzz',Value)));
- end;
- procedure TJSONIniFile.WriteDateTime(const Section, Ident: string; Value: TDateTime);
- begin
- SetKeyData(Section,Ident,CreateJSON(FormatDateTime('yyyy"-"mm"-"dd"T"hh":"nn":"ss.zzz',Value)));
- end;
- procedure TJSONIniFile.WriteFloat(const Section, Ident: string; Value: Double);
- begin
- SetKeyData(Section,Ident,CreateJSON(Value));
- end;
- procedure TJSONIniFile.WriteTime(const Section, Ident: string; Value: TDateTime);
- begin
- SetKeyData(Section,Ident,CreateJSON(FormatDateTime('0000"-"00"-"00"T"hh":"nn":"ss.zzz',Value)));
- end;
- procedure TJSONIniFile.WriteInteger(const Section, Ident: string; Value: Longint);
- begin
- SetKeyData(Section,Ident,CreateJSON(Value));
- end;
- procedure TJSONIniFile.WriteInt64(const Section, Ident: string; Value: Int64);
- begin
- SetKeyData(Section,Ident,CreateJSON(Value));
- end;
- procedure TJSONIniFile.WriteBool(const Section, Ident: string; Value: Boolean);
- begin
- SetKeyData(Section,Ident,CreateJSON(Value));
- end;
- procedure TJSONIniFile.ReadSection(const Section: string; Strings: TStrings);
- Var
- O : TJSONObject;
- E : TJSONEnum;
- begin
- O:=GetSection(Section,False);
- if Assigned(O) then
- For E in O do
- If (E.Value.JSONType in ActualValueJSONTypes) then
- Strings.Add(E.Key);
- end;
- procedure TJSONIniFile.ReadSections(Strings: TStrings);
- Var
- R : TJSONObject;
- E : TJSONEnum;
- begin
- R:=GetRoot;
- for E in R do
- if E.Value.JSONType=jtObject then
- Strings.Add(E.Key);
- end;
- procedure TJSONIniFile.ReadSectionValues(const Section: string; Strings: TStrings; AOptions: TSectionValuesOptions);
- Var
- O : TJSONObject;
- E : TJSONEnum;
- V : TJSONStringType;
- begin
- O:=GetSection(Section,False);
- if Assigned(O) then
- For E in O do
- begin
- If (E.Value.JSONType in ActualValueJSONTypes) then
- begin
- V:=E.Value.AsString;
- Strings.Add(E.Key+'='+V);
- end
- else if (svoIncludeInvalid in AOptions) then
- begin
- V:=E.Value.AsJSON;
- Strings.Add(E.Key+'='+V);
- end
- end;
- end;
- procedure TJSONIniFile.EraseSection(const Section: string);
- Var
- I : Integer;
- begin
- I:=GetRoot.IndexOfName(Section,True);
- if (I<>-1) then
- begin
- GetRoot.Delete(I);
- MaybeUpdateFile;
- end;
- end;
- procedure TJSONIniFile.DeleteKey(const Section, Ident: String);
- Var
- O : TJSONObject;
- I : integer;
- begin
- O:=GetSection(Section,False);
- if O<>Nil then
- begin
- I:=O.IndexOfName(Ident,True);
- if I<>-1 then
- begin
- O.Delete(I);
- MaybeUpdateFile;
- end;
- end;
- end;
- procedure TJSONIniFile.UpdateFile;
- begin
- If (FileName<>'') then
- UpdateFile(FileName)
- end;
- procedure TJSONIniFile.UpdateFile(const AFileName: string);
- Var
- S : TJSONStringType;
- begin
- With TFileStream.Create(AFileName,fmCreate) do
- try
- S:=FJSON.FormatJSON();
- WriteBuffer(S[1],Length(S));
- finally
- Free;
- end;
- end;
- end.
|