mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-18 13:29:19 +02:00
850 lines
21 KiB
ObjectPascal
850 lines
21 KiB
ObjectPascal
{
|
|
This file is part of the Free Component Library
|
|
|
|
Implementation of TJSONConfig class
|
|
Copyright (c) 2007 Michael Van Canneyt michael@freepascal.org
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
**********************************************************************}
|
|
|
|
{
|
|
TJSONConfig enables applications to use JSON files for storing their
|
|
configuration data
|
|
}
|
|
|
|
{$IFDEF FPC}
|
|
{$MODE objfpc}
|
|
{$H+}
|
|
{$ENDIF}
|
|
|
|
unit jsonConf;
|
|
|
|
interface
|
|
|
|
uses
|
|
SysUtils, Classes, fpjson, jsonscanner, jsonparser;
|
|
|
|
Const
|
|
DefaultJSONOptions = [joUTF8,joComments];
|
|
|
|
type
|
|
EJSONConfigError = class(Exception);
|
|
|
|
(* ********************************************************************
|
|
"APath" is the path and name of a value: A JSON configuration file
|
|
is hierachical. "/" is the path delimiter, the part after the last
|
|
"/" is the name of the value. The path components will be mapped
|
|
to nested JSON objects, with the name equal to the part. In practice
|
|
this means that "/my/path/value" will be written as:
|
|
{
|
|
"my" : {
|
|
"path" : {
|
|
"value" : Value
|
|
}
|
|
}
|
|
}
|
|
******************************************************************** *)
|
|
|
|
{ TJSONConfig }
|
|
|
|
TJSONConfig = class(TComponent)
|
|
private
|
|
FFilename: String;
|
|
FFormatIndentSize: Integer;
|
|
FFormatoptions: TFormatOptions;
|
|
FFormatted: Boolean;
|
|
FJSONOptions: TJSONOptions;
|
|
FKey: TJSONObject;
|
|
procedure DoSetFilename(const AFilename: String; ForceReload: Boolean);
|
|
procedure SetFilename(const AFilename: String);
|
|
procedure SetJSONOptions(AValue: TJSONOptions);
|
|
Function StripSlash(Const P : UnicodeString) : UnicodeString;
|
|
protected
|
|
FJSON: TJSONObject;
|
|
FModified: Boolean;
|
|
Procedure LoadFromFile(Const AFileName : String);
|
|
Procedure LoadFromStream(S : TStream); virtual;
|
|
procedure Loaded; override;
|
|
function FindNodeForValue(const APath: UnicodeString; aExpectedType: TJSONDataClass; out AParent: TJSONObject; out ElName: UnicodeString): TJSONData;
|
|
function FindPath(Const APath: UnicodeString; AllowCreate : Boolean) : TJSONObject;
|
|
function FindObject(Const APath: UnicodeString; AllowCreate : Boolean) : TJSONObject;
|
|
function FindObject(Const APath: UnicodeString; AllowCreate : Boolean;Out ElName : UnicodeString) : TJSONObject;
|
|
function FindElement(Const APath: UnicodeString; CreateParent : Boolean; AllowObject : Boolean = False) : TJSONData;
|
|
function FindElement(Const APath: UnicodeString; CreateParent : Boolean; out AParent : TJSONObject; Out ElName : UnicodeString; AllowObject : Boolean = False) : TJSONData;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
Procedure Reload;
|
|
procedure Clear;
|
|
procedure Flush; // Writes the JSON file
|
|
procedure OpenKey(const aPath: UnicodeString; AllowCreate : Boolean);
|
|
procedure CloseKey;
|
|
procedure ResetKey;
|
|
Procedure EnumSubKeys(Const APath : UnicodeString; List : TStrings);
|
|
Procedure EnumValues(Const APath : UnicodeString; List : TStrings);
|
|
|
|
function GetValue(const APath: UnicodeString; const ADefault: UnicodeString): UnicodeString; overload;
|
|
function GetValue(const APath: RawByteString; const ADefault: RawByteString): UnicodeString; overload;
|
|
function GetValue(const APath: UnicodeString; ADefault: Integer): Integer; overload;
|
|
function GetValue(const APath: RawByteString; ADefault: Integer): Integer; overload;
|
|
function GetValue(const APath: UnicodeString; ADefault: Int64): Int64; overload;
|
|
function GetValue(const APath: RawByteString; ADefault: Int64): Int64; overload;
|
|
function GetValue(const APath: UnicodeString; ADefault: Boolean): Boolean; overload;
|
|
function GetValue(const APath: RawByteString; ADefault: Boolean): Boolean; overload;
|
|
function GetValue(const APath: UnicodeString; ADefault: Double): Double; overload;
|
|
function GetValue(const APath: RawByteString; ADefault: Double): Double; overload;
|
|
Function GetValue(const APath: UnicodeString; AValue: TStrings; Const ADefault: String) : Boolean; overload;
|
|
Function GetValue(const APath: RawByteString; AValue: TStrings; Const ADefault: String) : Boolean; overload;
|
|
Function GetValue(const APath: UnicodeString; AValue: TStrings; Const ADefault: TStrings): Boolean; overload;
|
|
|
|
procedure SetValue(const APath: UnicodeString; const AValue: UnicodeString); overload;
|
|
procedure SetValue(const APath: RawByteString; const AValue: RawByteString); overload;
|
|
procedure SetValue(const APath: UnicodeString; AValue: Integer); overload;
|
|
procedure SetValue(const APath: UnicodeString; AValue: Int64); overload;
|
|
procedure SetValue(const APath: UnicodeString; AValue: Boolean); overload;
|
|
procedure SetValue(const APath: UnicodeString; AValue: Double); overload;
|
|
procedure SetValue(const APath: UnicodeString; AValue: TStrings; AsObject : Boolean = False); overload;
|
|
|
|
procedure SetDeleteValue(const APath: UnicodeString; const AValue, DefValue: UnicodeString); overload;
|
|
procedure SetDeleteValue(const APath: UnicodeString; AValue, DefValue: Integer); overload;
|
|
procedure SetDeleteValue(const APath: UnicodeString; AValue, DefValue: Int64); overload;
|
|
procedure SetDeleteValue(const APath: UnicodeString; AValue, DefValue: Boolean); overload;
|
|
|
|
procedure DeletePath(const APath: UnicodeString);
|
|
procedure DeleteValue(const APath: UnicodeString);
|
|
property Modified: Boolean read FModified;
|
|
published
|
|
Property Filename: String read FFilename write SetFilename;
|
|
Property Formatted : Boolean Read FFormatted Write FFormatted;
|
|
Property FormatOptions : TFormatOptions Read FFormatoptions Write FFormatOptions Default DefaultFormat;
|
|
Property FormatIndentsize : Integer Read FFormatIndentSize Write FFormatIndentSize Default DefaultIndentSize;
|
|
Property JSONOptions : TJSONOptions Read FJSONOptions Write SetJSONOptions Default DefaultJSONOptions;
|
|
end;
|
|
|
|
|
|
// ===================================================================
|
|
|
|
implementation
|
|
|
|
Resourcestring
|
|
SErrInvalidJSONFile = '"%s" is not a valid JSON configuration file.';
|
|
SErrCouldNotOpenKey = 'Could not open key "%s".';
|
|
SErrCannotNotReplaceKey = 'A (sub)key with name "%s" already exists.';
|
|
|
|
constructor TJSONConfig.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FJSON:=TJSONObject.Create;
|
|
FKey:=FJSON;
|
|
FFormatOptions:=DefaultFormat;
|
|
FFormatIndentsize:=DefaultIndentSize;
|
|
FJSONOptions:=DefaultJSONOptions;
|
|
end;
|
|
|
|
destructor TJSONConfig.Destroy;
|
|
begin
|
|
if Assigned(FJSON) then
|
|
begin
|
|
Flush;
|
|
FreeANdNil(FJSON);
|
|
end;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TJSONConfig.Clear;
|
|
begin
|
|
FJSON.Clear;
|
|
FKey:=FJSON;
|
|
end;
|
|
|
|
procedure TJSONConfig.Flush;
|
|
|
|
Var
|
|
F : TFileStream;
|
|
S : TJSONStringType;
|
|
|
|
begin
|
|
if Modified then
|
|
begin
|
|
F:=TFileStream.Create(FileName,fmCreate);
|
|
Try
|
|
if Formatted then
|
|
S:=FJSON.FormatJSON(Formatoptions,FormatIndentSize)
|
|
else
|
|
S:=FJSON.AsJSON;
|
|
if S>'' then
|
|
F.WriteBuffer(S[1],Length(S));
|
|
Finally
|
|
F.Free;
|
|
end;
|
|
FModified := False;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TJSONConfig.FindObject(const APath: UnicodeString; AllowCreate: Boolean
|
|
): TJSONObject;
|
|
|
|
Var
|
|
Dummy : UnicodeString;
|
|
|
|
begin
|
|
Result:=FindObject(APath,AllowCreate,Dummy);
|
|
end;
|
|
|
|
function TJSONConfig.FindObject(const APath: UnicodeString; AllowCreate: Boolean;
|
|
out ElName: UnicodeString): TJSONObject;
|
|
|
|
Var
|
|
S,El : UnicodeString;
|
|
P,I : Integer;
|
|
T : TJSonObject;
|
|
|
|
begin
|
|
// Writeln('Looking for : ', APath);
|
|
S:=APath;
|
|
If Pos('/',S)=1 then
|
|
Result:=FJSON
|
|
else
|
|
Result:=FKey;
|
|
Repeat
|
|
P:=Pos('/',S);
|
|
If (P<>0) then
|
|
begin
|
|
// Only real paths, ignore double slash
|
|
If (P<>1) then
|
|
begin
|
|
El:=Copy(S,1,P-1);
|
|
If (Result.Count=0) then
|
|
I:=-1
|
|
else
|
|
I:=Result.IndexOfName(UTF8Encode(El));
|
|
If (I=-1) then
|
|
// No element with this name.
|
|
begin
|
|
If AllowCreate then
|
|
begin
|
|
// Create new node.
|
|
T:=Result;
|
|
Result:=TJSonObject.Create;
|
|
T.Add(UTF8Encode(El),Result);
|
|
end
|
|
else
|
|
Result:=Nil
|
|
end
|
|
else
|
|
// Node found, check if it is an object
|
|
begin
|
|
if (Result.Items[i].JSONtype=jtObject) then
|
|
Result:=Result.Objects[UTF8Encode(el)]
|
|
else
|
|
begin
|
|
// Writeln(el,' type wrong');
|
|
If AllowCreate then
|
|
begin
|
|
// Writeln('Creating ',el);
|
|
Result.Delete(I);
|
|
T:=Result;
|
|
Result:=TJSonObject.Create;
|
|
T.Add(UTF8Encode(El),Result);
|
|
end
|
|
else
|
|
Result:=Nil
|
|
end;
|
|
end;
|
|
end;
|
|
Delete(S,1,P);
|
|
end;
|
|
Until (P=0) or (Result=Nil);
|
|
ElName:=S;
|
|
end;
|
|
|
|
function TJSONConfig.FindElement(const APath: UnicodeString; CreateParent: Boolean; AllowObject : Boolean = False): TJSONData;
|
|
|
|
Var
|
|
O : TJSONObject;
|
|
ElName : UnicodeString;
|
|
|
|
begin
|
|
Result:=FindElement(APath,CreateParent,O,ElName,AllowObject);
|
|
end;
|
|
|
|
function TJSONConfig.FindElement(const APath: UnicodeString;
|
|
CreateParent: Boolean; out AParent: TJSONObject; out ElName: UnicodeString;
|
|
AllowObject : Boolean = False): TJSONData;
|
|
|
|
Var
|
|
I : Integer;
|
|
|
|
begin
|
|
Result:=Nil;
|
|
Aparent:=FindObject(APath,CreateParent,ElName);
|
|
If Assigned(Aparent) then
|
|
begin
|
|
// Writeln('Found parent, looking for element:',elName);
|
|
I:=AParent.IndexOfName(UTF8Encode(ElName));
|
|
// Writeln('Element index is',I);
|
|
If (I<>-1) And ((AParent.items[I].JSONType<>jtObject) or AllowObject) then
|
|
Result:=AParent.Items[i];
|
|
end;
|
|
// Writeln('Find ',aPath,' in "',FJSON.AsJSOn,'" : ',Elname,' : ',Result<>NIl);
|
|
end;
|
|
|
|
|
|
function TJSONConfig.GetValue(const APath: RawByteString; const ADefault: RawByteString): UnicodeString;
|
|
|
|
begin
|
|
Result:=GetValue(UTF8Decode(aPath),UTF8Decode(ADefault));
|
|
end;
|
|
|
|
function TJSONConfig.GetValue(const APath: UnicodeString; const ADefault: UnicodeString): UnicodeString;
|
|
|
|
var
|
|
El : TJSONData;
|
|
|
|
begin
|
|
El:=FindElement(StripSlash(APath),False);
|
|
If Assigned(El) then
|
|
Result:=El.AsUnicodeString
|
|
else
|
|
Result:=ADefault;
|
|
end;
|
|
|
|
function TJSONConfig.GetValue(const APath: RawByteString; ADefault: Integer): Integer;
|
|
|
|
begin
|
|
Result:=GetValue(UTF8Decode(aPath),ADefault);
|
|
end;
|
|
|
|
function TJSONConfig.GetValue(const APath: UnicodeString; ADefault: Integer): Integer;
|
|
var
|
|
El : TJSONData;
|
|
|
|
begin
|
|
El:=FindElement(StripSlash(APath),False);
|
|
If Not Assigned(el) then
|
|
Result:=ADefault
|
|
else if (el is TJSONNumber) then
|
|
Result:=El.AsInteger
|
|
else
|
|
Result:=StrToIntDef(El.AsString,ADefault);
|
|
end;
|
|
|
|
function TJSONConfig.GetValue(const APath: RawByteString; ADefault: Int64): Int64;
|
|
|
|
begin
|
|
Result:=GetValue(UTF8Decode(aPath),ADefault);
|
|
end;
|
|
|
|
function TJSONConfig.GetValue(const APath: UnicodeString; ADefault: Int64): Int64;
|
|
var
|
|
El : TJSONData;
|
|
|
|
begin
|
|
El:=FindElement(StripSlash(APath),False);
|
|
If Not Assigned(el) then
|
|
Result:=ADefault
|
|
else if (el is TJSONNumber) then
|
|
Result:=El.AsInt64
|
|
else
|
|
Result:=StrToInt64Def(El.AsString,ADefault);
|
|
end;
|
|
|
|
function TJSONConfig.GetValue(const APath: RawByteString; ADefault: Boolean): Boolean;
|
|
|
|
begin
|
|
Result:=GetValue(UTF8Decode(aPath),ADefault);
|
|
end;
|
|
|
|
function TJSONConfig.GetValue(const APath: UnicodeString; ADefault: Boolean): Boolean;
|
|
|
|
var
|
|
El : TJSONData;
|
|
|
|
begin
|
|
El:=FindElement(StripSlash(APath),False);
|
|
If Not Assigned(el) then
|
|
Result:=ADefault
|
|
else if (el is TJSONBoolean) then
|
|
Result:=El.AsBoolean
|
|
else
|
|
Result:=StrToBoolDef(El.AsString,ADefault);
|
|
end;
|
|
|
|
function TJSONConfig.GetValue(const APath: RawByteString; ADefault: Double): Double;
|
|
|
|
begin
|
|
Result:=GetValue(UTF8Decode(aPath),ADefault);
|
|
end;
|
|
|
|
function TJSONConfig.GetValue(const APath: UnicodeString; ADefault: Double): Double;
|
|
|
|
var
|
|
El : TJSONData;
|
|
|
|
begin
|
|
El:=FindElement(StripSlash(APath),False);
|
|
If Not Assigned(el) then
|
|
Result:=ADefault
|
|
else if (el is TJSONNumber) then
|
|
Result:=El.AsFloat
|
|
else
|
|
Result:=StrToFloatDef(El.AsString,ADefault);
|
|
end;
|
|
|
|
function TJSONConfig.GetValue(const APath: RawByteString; AValue: TStrings;
|
|
const ADefault: String): Boolean;
|
|
|
|
begin
|
|
Result:=GetValue(UTF8Decode(aPath),AValue, ADefault);
|
|
end;
|
|
|
|
|
|
function TJSONConfig.GetValue(const APath: UnicodeString; AValue: TStrings;
|
|
const ADefault: String): Boolean;
|
|
var
|
|
El : TJSONData;
|
|
D : TJSONEnum;
|
|
|
|
begin
|
|
AValue.Clear;
|
|
El:=FindElement(StripSlash(APath),False,True);
|
|
Result:=Assigned(el);
|
|
If Not Result then
|
|
begin
|
|
AValue.Text:=ADefault;
|
|
exit;
|
|
end;
|
|
Case El.JSONType of
|
|
jtArray:
|
|
For D in El do
|
|
if D.Value.JSONType in ActualValueJSONTypes then
|
|
AValue.Add(D.Value.AsString);
|
|
jtObject:
|
|
For D in El do
|
|
if D.Value.JSONType in ActualValueJSONTypes then
|
|
AValue.Add(D.Key+'='+D.Value.AsString);
|
|
else
|
|
AValue.Text:=EL.AsString
|
|
end;
|
|
|
|
end;
|
|
|
|
function TJSONConfig.GetValue(const APath: UnicodeString; AValue: TStrings;
|
|
const ADefault: TStrings): Boolean;
|
|
begin
|
|
Result:=GetValue(APath,AValue,'');
|
|
If Not Result then
|
|
AValue.Assign(ADefault);
|
|
end;
|
|
|
|
|
|
procedure TJSONConfig.SetValue(const APath: UnicodeString; const AValue: UnicodeString);
|
|
|
|
var
|
|
El : TJSONData;
|
|
ElName : UnicodeString;
|
|
O : TJSONObject;
|
|
|
|
begin
|
|
El:=FindNodeForValue(aPath,TJSONString,O,elName);
|
|
If Not Assigned(el) then
|
|
begin
|
|
El:=TJSONString.Create(AValue);
|
|
O.Add(UTF8Encode(ElName),El);
|
|
end
|
|
else
|
|
El.AsUnicodeString:=AValue;
|
|
FModified:=True;
|
|
end;
|
|
|
|
|
|
procedure TJSONConfig.SetValue(const APath: RawByteString;
|
|
const AValue: RawByteString);
|
|
begin
|
|
SetValue(UTF8Decode(APath),UTF8Decode(AValue));
|
|
end;
|
|
|
|
procedure TJSONConfig.SetDeleteValue(const APath: UnicodeString; const AValue, DefValue: UnicodeString);
|
|
begin
|
|
if AValue = DefValue then
|
|
DeleteValue(APath)
|
|
else
|
|
SetValue(APath, AValue);
|
|
end;
|
|
|
|
procedure TJSONConfig.SetValue(const APath: UnicodeString; AValue: Integer);
|
|
|
|
var
|
|
El : TJSONData;
|
|
ElName : UnicodeString;
|
|
O : TJSONObject;
|
|
|
|
begin
|
|
El:=FindNodeForValue(aPath,TJSONIntegerNumber,O,elName);
|
|
If Not Assigned(el) then
|
|
begin
|
|
El:=TJSONIntegerNumber.Create(AValue);
|
|
O.Add(UTF8Encode(ElName),El);
|
|
end
|
|
else
|
|
El.AsInteger:=AValue;
|
|
FModified:=True;
|
|
end;
|
|
|
|
procedure TJSONConfig.SetValue(const APath: UnicodeString; AValue: Int64);
|
|
|
|
var
|
|
El : TJSONData;
|
|
ElName : UnicodeString;
|
|
O : TJSONObject;
|
|
|
|
begin
|
|
El:=FindNodeForValue(aPath,TJSONInt64Number,O,elName);
|
|
If Not Assigned(el) then
|
|
begin
|
|
El:=TJSONInt64Number.Create(AValue);
|
|
O.Add(UTF8Encode(ElName),El);
|
|
end
|
|
else
|
|
El.AsInt64:=AValue;
|
|
FModified:=True;
|
|
end;
|
|
|
|
procedure TJSONConfig.SetDeleteValue(const APath: UnicodeString; AValue,
|
|
DefValue: Integer);
|
|
begin
|
|
if AValue = DefValue then
|
|
DeleteValue(APath)
|
|
else
|
|
SetValue(APath, AValue);
|
|
end;
|
|
|
|
procedure TJSONConfig.SetDeleteValue(const APath: UnicodeString; AValue,
|
|
DefValue: Int64);
|
|
begin
|
|
if AValue = DefValue then
|
|
DeleteValue(APath)
|
|
else
|
|
SetValue(APath, AValue);
|
|
end;
|
|
|
|
procedure TJSONConfig.SetValue(const APath: UnicodeString; AValue: Boolean);
|
|
|
|
var
|
|
El : TJSONData;
|
|
ElName : UnicodeString;
|
|
O : TJSONObject;
|
|
|
|
begin
|
|
El:=FindNodeForValue(aPath,TJSONBoolean,O,elName);
|
|
If Not Assigned(el) then
|
|
begin
|
|
El:=TJSONBoolean.Create(AValue);
|
|
O.Add(UTF8Encode(ElName),El);
|
|
end
|
|
else
|
|
El.AsBoolean:=AValue;
|
|
FModified:=True;
|
|
end;
|
|
|
|
procedure TJSONConfig.SetValue(const APath: UnicodeString; AValue: Double);
|
|
|
|
var
|
|
El : TJSONData;
|
|
ElName : UnicodeString;
|
|
O : TJSONObject;
|
|
|
|
begin
|
|
El:=FindNodeForValue(aPath,TJSONFloatNumber,O,elName);
|
|
If Not Assigned(el) then
|
|
begin
|
|
El:=TJSONFloatNumber.Create(AValue);
|
|
O.Add(UTF8Encode(ElName),El);
|
|
end
|
|
else
|
|
El.AsFloat:=AValue;
|
|
FModified:=True;
|
|
end;
|
|
|
|
procedure TJSONConfig.SetValue(const APath: UnicodeString; AValue: TStrings; AsObject : Boolean = False);
|
|
|
|
var
|
|
El : TJSONData;
|
|
ElName : UnicodeString;
|
|
O : TJSONObject;
|
|
I : integer;
|
|
A : TJSONArray;
|
|
N,V : String;
|
|
|
|
begin
|
|
if AsObject then
|
|
El:=FindNodeForValue(aPath,TJSONObject,O,elName)
|
|
else
|
|
El:=FindNodeForValue(aPath,TJSONArray,O,elName);
|
|
If Not Assigned(el) then
|
|
begin
|
|
if AsObject then
|
|
El:=TJSONObject.Create
|
|
else
|
|
El:=TJSONArray.Create;
|
|
O.Add(UTF8Encode(ElName),El);
|
|
end;
|
|
if Not AsObject then
|
|
begin
|
|
A:=El as TJSONArray;
|
|
A.Clear;
|
|
For N in Avalue do
|
|
A.Add(N);
|
|
end
|
|
else
|
|
begin
|
|
O:=El as TJSONObject;
|
|
For I:=0 to AValue.Count-1 do
|
|
begin
|
|
AValue.GetNameValue(I,N,V);
|
|
O.Add(N,V);
|
|
end;
|
|
end;
|
|
FModified:=True;
|
|
end;
|
|
|
|
procedure TJSONConfig.SetDeleteValue(const APath: UnicodeString; AValue,
|
|
DefValue: Boolean);
|
|
begin
|
|
if AValue = DefValue then
|
|
DeleteValue(APath)
|
|
else
|
|
SetValue(APath,AValue);
|
|
end;
|
|
|
|
procedure TJSONConfig.DeletePath(const APath: UnicodeString);
|
|
|
|
Var
|
|
P : UnicodeString;
|
|
L : integer;
|
|
Node : TJSONObject;
|
|
ElName : UnicodeString;
|
|
|
|
begin
|
|
P:=StripSlash(APath);
|
|
L:=Length(P);
|
|
If (L>0) then
|
|
begin
|
|
Node := FindObject(P,False,ElName);
|
|
If Assigned(Node) then
|
|
begin
|
|
L:=Node.IndexOfName(UTF8Encode(ElName));
|
|
If (L<>-1) then
|
|
Node.Delete(L);
|
|
end;
|
|
end;
|
|
FModified:=True;
|
|
end;
|
|
|
|
procedure TJSONConfig.DeleteValue(const APath: UnicodeString);
|
|
|
|
begin
|
|
DeletePath(APath);
|
|
end;
|
|
|
|
procedure TJSONConfig.Reload;
|
|
|
|
begin
|
|
if Length(Filename) > 0 then
|
|
DoSetFilename(Filename,True);
|
|
end;
|
|
|
|
procedure TJSONConfig.Loaded;
|
|
begin
|
|
inherited Loaded;
|
|
Reload;
|
|
end;
|
|
|
|
function TJSONConfig.FindNodeForValue(const APath: UnicodeString; aExpectedType : TJSONDataClass; out AParent: TJSONObject; out ElName: UnicodeString): TJSONData;
|
|
|
|
var
|
|
I : Integer;
|
|
|
|
begin
|
|
Result:=FindElement(StripSlash(APath),True,aParent,ElName,True);
|
|
if Assigned(Result) and Not Result.InheritsFrom(aExpectedType) then
|
|
begin
|
|
I:=aParent.IndexOfName(UTF8Encode(elName));
|
|
aParent.Delete(i);
|
|
Result:=Nil;
|
|
end;
|
|
end;
|
|
|
|
function TJSONConfig.FindPath(const APath: UnicodeString; AllowCreate: Boolean
|
|
): TJSONObject;
|
|
|
|
Var
|
|
P : UnicodeString;
|
|
L : Integer;
|
|
|
|
begin
|
|
P:=APath;
|
|
L:=Length(P);
|
|
If (L=0) or (P[L]<>'/') then
|
|
P:=P+'/';
|
|
Result:=FindObject(P,AllowCreate);
|
|
end;
|
|
|
|
procedure TJSONConfig.DoSetFilename(const AFilename: String; ForceReload: Boolean);
|
|
|
|
begin
|
|
if (not ForceReload) and (FFilename = AFilename) then
|
|
exit;
|
|
FFilename := AFilename;
|
|
|
|
if csLoading in ComponentState then
|
|
exit;
|
|
Flush;
|
|
If Not FileExists(AFileName) then
|
|
Clear
|
|
else
|
|
LoadFromFile(AFileName);
|
|
end;
|
|
|
|
procedure TJSONConfig.SetFilename(const AFilename: String);
|
|
begin
|
|
DoSetFilename(AFilename, False);
|
|
end;
|
|
|
|
procedure TJSONConfig.SetJSONOptions(AValue: TJSONOptions);
|
|
begin
|
|
if FJSONOptions=AValue then Exit;
|
|
FJSONOptions:=AValue;
|
|
if csLoading in ComponentState then
|
|
exit;
|
|
if (FFileName<>'') then
|
|
Reload;
|
|
end;
|
|
|
|
function TJSONConfig.StripSlash(const P: UnicodeString): UnicodeString;
|
|
|
|
Var
|
|
L : Integer;
|
|
|
|
begin
|
|
L:=Length(P);
|
|
If (L>0) and (P[l]='/') then
|
|
Result:=Copy(P,1,L-1)
|
|
else
|
|
Result:=P;
|
|
end;
|
|
|
|
procedure TJSONConfig.LoadFromFile(const AFileName: String);
|
|
|
|
Var
|
|
F : TFileStream;
|
|
|
|
begin
|
|
F:=TFileStream.Create(AFileName,fmopenRead or fmShareDenyWrite);
|
|
try
|
|
LoadFromStream(F);
|
|
finally
|
|
F.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TJSONConfig.LoadFromStream(S: TStream);
|
|
|
|
Var
|
|
P : TJSONParser;
|
|
J : TJSONData;
|
|
|
|
begin
|
|
P:=TJSONParser.Create(S,FJSONOptions);
|
|
try
|
|
J:=P.Parse;
|
|
If (J is TJSONObject) then
|
|
begin
|
|
FreeAndNil(FJSON);
|
|
FJSON:=J as TJSONObject;
|
|
FKey:=FJSON;
|
|
end
|
|
else
|
|
begin
|
|
FreeAndNil(J);
|
|
Raise EJSONConfigError.CreateFmt(SErrInvalidJSONFile,[FileName]);
|
|
end;
|
|
finally
|
|
P.Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TJSONConfig.CloseKey;
|
|
begin
|
|
ResetKey;
|
|
end;
|
|
|
|
procedure TJSONConfig.OpenKey(const aPath: UnicodeString; AllowCreate: Boolean);
|
|
|
|
Var
|
|
P : UnicodeString;
|
|
L : Integer;
|
|
|
|
begin
|
|
P:=APath;
|
|
L:=Length(P);
|
|
If (L=0) then
|
|
FKey:=FJSON
|
|
else
|
|
begin
|
|
if (P[L]<>'/') then
|
|
P:=P+'/';
|
|
FKey:=FindObject(P,AllowCreate);
|
|
If (FKey=Nil) Then
|
|
Raise EJSONConfigError.CreateFmt(SErrCouldNotOpenKey,[APath]);
|
|
end;
|
|
end;
|
|
|
|
procedure TJSONConfig.ResetKey;
|
|
begin
|
|
FKey:=FJSON;
|
|
end;
|
|
|
|
procedure TJSONConfig.EnumSubKeys(const APath: UnicodeString; List: TStrings);
|
|
|
|
Var
|
|
AKey : TJSONObject;
|
|
I : Integer;
|
|
|
|
begin
|
|
AKey:=FindPath(APath,False);
|
|
If Assigned(AKey) then
|
|
begin
|
|
For I:=0 to AKey.Count-1 do
|
|
If AKey.Items[i] is TJSONObject then
|
|
List.Add(AKey.Names[i]);
|
|
end;
|
|
end;
|
|
|
|
procedure TJSONConfig.EnumValues(const APath: UnicodeString; List: TStrings);
|
|
|
|
Var
|
|
AKey : TJSONObject;
|
|
I : Integer;
|
|
|
|
begin
|
|
AKey:=FindPath(APath,False);
|
|
If Assigned(AKey) then
|
|
begin
|
|
For I:=0 to AKey.Count-1 do
|
|
If Not (AKey.Items[i] is TJSONObject) then
|
|
List.Add(AKey.Names[i]);
|
|
end;
|
|
end;
|
|
|
|
|
|
end.
|