unit SimplyJSON;
{$MODE Delphi}
{$codepage UTF8}
{*************************************************************************************************************************************
                                                      SimplyJSON
                                    Модуль для парсинга JSON файлов (используется TJSONConfig).
                                        Авторские права (c) 2016 - 2019, Александр Бабаев.
История изменений:
1.3 (01.05.2019) Переработан алгоритм открытия / парсинга JSON файлов при чтении, изменена функция JSReadFont
1.2 (09.05.2017) Добавлены функции чтения/записи шрифтов.
*************************************************************************************************************************************}
interface
uses Classes, SysUtils, Graphics, GraphicsEx, LazFileUtils, fpjson, jsonparser, jsonConf, ANBFormatString;
//stdcalls
function JSReadString (const AKey, ADefault: UnicodeString; const AFileName: String): UnicodeString; STDCALL; OVERLOAD;
function JSReadString (const AKey, ADefault, AFileName: String): String; STDCALL; OVERLOAD;
function JSReadInteger (const AKey: UnicodeString; const ADefault: Int64; const AFileName: String): Int64; STDCALL; OVERLOAD;
function JSReadInteger (const AKey: String; const ADefault: Int64; const AFileName: String): Int64; STDCALL; OVERLOAD;
function JSReadBoolean (const AKey: UnicodeString; const ADefault: Boolean; const AFileName: String): Boolean; STDCALL; OVERLOAD;
function JSReadBoolean (const AKey: String; const ADefault: Boolean; const AFileName: String): Boolean; STDCALL; OVERLOAD;
function JSReadRGBColor (const AKey: UnicodeString; const ADefault: TRGBColor; const AFileName: String): TRGBColor; STDCALL; OVERLOAD;
function JSReadRGBColor (const AKey: String; const ADefault: TRGBColor; const AFileName: String): TRGBColor; STDCALL; OVERLOAD;
procedure JSReadFont (const AKey: UnicodeString; var Font: TFont; const AFileName: String); STDCALL; OVERLOAD;
procedure JSReadFont (const AKey: String; var Font: TFont; const AFileName: String); STDCALL; OVERLOAD;
procedure JSWriteString (const AKey, AValue: UnicodeString; const AFileName: String); STDCALL; OVERLOAD;
procedure JSWriteString (const AKey, AValue, AFileName: String); STDCALL; OVERLOAD;
procedure JSWriteInteger (const AKey: UnicodeString; const AValue: Int64; const AFileName: String); STDCALL; OVERLOAD;
procedure JSWriteInteger (const AKey: String; const AValue: Int64; const AFileName: String); STDCALL; OVERLOAD;
procedure JSWriteBoolean (const AKey: UnicodeString; const AValue: Boolean; const AFileName: String); STDCALL; OVERLOAD;
procedure JSWriteBoolean (const AKey: String; const AValue: Boolean; const AFileName: String); STDCALL; OVERLOAD;
procedure JSWriteRGBColor (const AKey: UnicodeString; const AValue: TRGBColor; const AFileName: String); STDCALL; OVERLOAD;
procedure JSWriteRGBColor (const AKey: String; const AValue: TRGBColor; const AFileName: String); STDCALL; OVERLOAD;
procedure JSWriteFont (const AKey: UnicodeString; const AValue: TFont; const AFileName: String); STDCALL; OVERLOAD;
procedure JSWriteFont (const AKey: String; const AValue: TFont; const AFileName: String); STDCALL; OVERLOAD;
procedure JSDeleteKey (const AKey: UnicodeString; const AFileName: String); STDCALL; OVERLOAD;
procedure JSDeleteKey (const AKey, AFileName: String); STDCALL; OVERLOAD;
procedure JSReadSubKeys (const AKey: UnicodeString; SubkeyList: TStrings; const AFileName: String); STDCALL; OVERLOAD;
procedure JSReadSubKeys (const AKey: String; SubkeyList: TStrings; const AFileName: String); STDCALL; OVERLOAD;
implementation
//Support functions
procedure GetJSData (const AFileName: String; var JSData: TJSONData);
var FS: TFileStream;
    SL: TStringList;
begin
if not FileExistsUTF8(AFileName) then
 begin
 FS:= TFileStream.Create(AFileName, fmOpenWrite);
 SL:= TStringList.Create;
 SL.Text:= '{}';
 SL.SaveToStream(FS);
 SL.Free;
 FS.Free;
 end;
FS:= TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone);
JSData:= GetJSON(FS, True);
FS.Free;
end;
function FrmtKey (const AKey: UnicodeString): UnicodeString;
var KeyM: UnicodeString;
begin
Result:= AKey;
//Для совместимости с SimplyJSON 1.0 - 1.2 и операциями записи
KeyM:= AKey;
if KeyM[1] = '/' then
 Delete(KeyM, 1, 1);
Result:= UnicodeString(FormatStr(KeyM, ['/'], ['.']));
end;
//Read functions
function JSReadString (const AKey, ADefault: UnicodeString; const AFileName: String): UnicodeString; OVERLOAD;
var JD: TJSONData;
begin
GetJSData(AFileName, JD);
try
 Result:= JD.FindPath(FrmtKey(AKey)).AsUnicodeString;
 except
 Result:= ADefault;
 end;
JD.Free;
end;
function JSReadString (const AKey, ADefault, AFileName: String): String; OVERLOAD;
var Key, Def, Res: UnicodeString;
begin
Key:= UnicodeString(AKey);
Def:= UnicodeString(ADefault);
Res:= JSReadString(Key, Def, AFileName);
Result:= String(Res);
end;
function JSReadInteger (const AKey: UnicodeString; const ADefault: Int64; const AFileName: String): Int64; OVERLOAD;
var JD: TJSONData;
begin
GetJSData(AFileName, JD);
try
 Result:= JD.FindPath(FrmtKey(AKey)).AsInt64;
 except
 Result:= ADefault;
 end;
JD.Free;
end;
function JSReadInteger (const AKey: String; const ADefault: Int64; const AFileName: String): Int64; OVERLOAD;
var Key: UnicodeString;
begin
Key:= UnicodeString(AKey);
Result:= JSReadInteger(Key, ADefault, AFileName);
end;
function JSReadBoolean (const AKey: UnicodeString; const ADefault: Boolean; const AFileName: String): Boolean; OVERLOAD;
var JD: TJSONData;
begin
GetJSData(AFileName, JD);
try
 Result:= JD.FindPath(FrmtKey(AKey)).AsBoolean;
 except
 Result:= ADefault;
 end;
JD.Free;
end;
function JSReadBoolean (const AKey: String; const ADefault: Boolean; const AFileName: String): Boolean; OVERLOAD;
var Key: UnicodeString;
begin
Key:= UnicodeString(AKey);
Result:= JSReadBoolean(Key, ADefault, AFileName);
end;
function JSReadRGBColor (const AKey: UnicodeString; const ADefault: TRGBColor; const AFileName: String): TRGBColor; OVERLOAD;
var JD: TJSONData;
    KeyM: UnicodeString;
begin
GetJSData(AFileName, JD);
KeyM:= FrmtKey(AKey);
try
 Result.R:= JD.FindPath(KeyM + '.r').AsInteger;
 Result.G:= JD.FindPath(KeyM + '.g').AsInteger;
 Result.B:= JD.FindPath(KeyM + '.b').AsInteger;
 except
 Result:= ADefault;
 end;
JD.Free;
end;
function JSReadRGBColor (const AKey: String; const ADefault: TRGBColor; const AFileName: String): TRGBColor; OVERLOAD;
var Key: UnicodeString;
begin
Key:= UnicodeString(AKey);
Result:= JSReadRGBColor(Key, ADefault, AFileName);
end;
procedure JSReadFont (const AKey: UnicodeString; var Font: TFont; const AFileName: String);
var JD: TJSONData;
    FStyle: TFontStyles;
    KeyM: UnicodeString;
begin
GetJSData(AFileName, JD);
KeyM:= FrmtKey(AKey);
try
 Font.CharSet:= TFontCharSet(JD.FindPath(KeyM + '.charset').AsInteger);
 Font.Color:= StringToColor(JD.FindPath(KeyM + '.color').AsString);
 Font.Height:= JD.FindPath(KeyM + '.height').AsInteger;
 Font.Name:= JD.FindPath(KeyM + '.name').AsString;
 Font.Orientation:= JD.FindPath(KeyM + '.orientation').AsInteger;
 Font.Pitch:= TFontPitch(JD.FindPath(KeyM + '.pitch').AsInteger);
 Font.Quality:= TFontQuality(JD.FindPath(KeyM + '.quality').AsInteger);
 Font.Size:= JD.FindPath(KeyM + '.size').AsInteger;
 FStyle:= [];
 if (JD.FindPath(KeyM + '.style.bold').AsBoolean) then
  FStyle:= FStyle + [fsBold];
 if (JD.FindPath(KeyM + '.style.italic').AsBoolean) then
  FStyle:= FStyle + [fsItalic];
 if (JD.FindPath(KeyM + '.style.underline').AsBoolean) then
  FStyle:= FStyle + [fsUnderline];
 if (JD.FindPath(KeyM + '.style.strikeout').AsBoolean) then
  FStyle:= FStyle + [fsStrikeOut];
 Font.Style:= FStyle;
 except
 end;
JD.Free;
end;
procedure JSReadFont (const AKey: String; var Font: TFont; const AFileName: String);
var Key: UnicodeString;
begin
Key:= UnicodeString(AKey);
JSReadFont(Key, Font, AFileName);
end;
//Write procedures
procedure JSWriteString (const AKey, AValue: UnicodeString; const AFileName: String); OVERLOAD;
var JSConf: TJSONConfig;
begin
JSConf:= TJSONConfig.Create(Nil);
with JSConf do
 begin
 Filename:= AFileName;
 Formatted:= True;
 SetValue(AKey, AValue);
 Flush;
 Free;
 end;
end;
procedure JSWriteString (const AKey, AValue, AFileName: String); OVERLOAD;
var Key, Val: UnicodeString;
begin
Key:= UnicodeString(AKey);
Val:= UnicodeString(AValue);
JSWriteString(Key, Val, AFileName);
end;
procedure JSWriteInteger (const AKey: UnicodeString; const AValue: Int64; const AFileName: String); OVERLOAD;
var JSConf: TJSONConfig;
begin
JSConf:= TJSONConfig.Create(Nil);
with JSConf do
 begin
 Filename:= AFileName;
 Formatted:= True;
 SetValue(AKey, AValue);
 Free;
 end;
end;
procedure JSWriteInteger (const AKey: String; const AValue: Int64; const AFileName: String); OVERLOAD;
var Key: UnicodeString;
begin
Key:= UnicodeString(AKey);
JSWriteInteger(Key, AValue, AFileName);
end;
procedure JSWriteBoolean (const AKey: UnicodeString; const AValue: Boolean; const AFileName: String); OVERLOAD;
var JSConf: TJSONConfig;
begin
JSConf:= TJSONConfig.Create(Nil);
with JSConf do
 begin
 Filename:= AFileName;
 Formatted:= True;
 SetValue(AKey, AValue);
 Free;
 end;
end;
procedure JSWriteBoolean (const AKey: String; const AValue: Boolean; const AFileName: String); OVERLOAD;
var Key: UnicodeString;
begin
Key:= UnicodeString(AKey);
JSWriteBoolean(Key, AValue, AFileName);
end;
procedure JSWriteRGBColor (const AKey: UnicodeString; const AValue: TRGBColor; const AFileName: String); OVERLOAD;
var JSConf: TJSONConfig;
begin
JSConf:= TJSONConfig.Create(Nil);
with JSConf do
 begin
 Filename:= AFileName;
 Formatted:= True;
 SetValue(AKey + '/r', AValue.R);
 SetValue(AKey + '/g', AValue.G);
 SetValue(AKey + '/b', AValue.B);
 Free;
 end;
end;
procedure JSWriteRGBColor (const AKey: String; const AValue: TRGBColor; const AFileName: String); OVERLOAD;
var Key: UnicodeString;
begin
Key:= UnicodeString(AKey);
JSWriteRGBColor(Key, AValue, AFileName);
end;
procedure JSWriteFont (const AKey: UnicodeString; const AValue: TFont; const AFileName: String); OVERLOAD;
var JSConf: TJSONConfig;
begin
JSConf:= TJSONConfig.Create(Nil);
with JSConf do
 begin
 Filename:= AFileName;
 Formatted:= True;
 SetValue(AKey + '/charset', Integer(AValue.CharSet));
 SetValue(AKey + '/color', ColorToString(AValue.Color));
 SetValue(AKey + '/height', AValue.Height);
 SetValue(AKey + '/name', AValue.Name);
 SetValue(AKey + '/orientation', AValue.Orientation);
 SetValue(AKey + '/pitch', Integer(AValue.Pitch));
 SetValue(AKey + '/quality', Integer(AValue.Quality));
 SetValue(AKey + '/size', AValue.Size);
 SetValue(AKey + '/style/bold', (fsBold in AValue.Style));
 SetValue(AKey + '/style/italic', (fsItalic in AValue.Style));
 SetValue(AKey + '/style/underline', (fsUnderline in AValue.Style));
 SetValue(AKey + '/style/strikeout', (fsStrikeOut in AValue.Style));
 Free;
 end;
end;
procedure JSWriteFont (const AKey: String; const AValue: TFont; const AFileName: String); OVERLOAD;
var Key: UnicodeString;
begin
Key:= UnicodeString(AKey);
JSWriteFont(Key, AValue, AFileName);
end;
//Delete function
procedure JSDeleteKey (const AKey: UnicodeString; const AFileName: String); OVERLOAD;
var JSConf: TJSONConfig;
begin
JSConf:= TJSONConfig.Create(Nil);
with JSConf do
 begin
 Filename:= AFileName;
 Formatted:= True;
 DeletePath(AKey);
 Free;
 end;
end;
procedure JSDeleteKey (const AKey, AFileName: String); OVERLOAD;
var Key: UnicodeString;
begin
Key:= UnicodeString(AKey);
JSDeleteKey(Key, AFileName);
end;
//Read subkeys
procedure JSReadSubKeys (const AKey: UnicodeString; SubkeyList: TStrings; const AFileName: String); OVERLOAD;
var JSConf: TJSONConfig;
begin
JSConf:= TJSONConfig.Create(Nil);
with JSConf do
 begin
 Filename:= AFileName;
 Formatted:= True;
 SubkeyList.Clear;
 EnumSubKeys(AKey, SubkeyList);
 Free;
 end;
end;
procedure JSReadSubKeys (const AKey: String; SubkeyList: TStrings; const AFileName: String); OVERLOAD;
var Key: UnicodeString;
begin
Key:= UnicodeString(AKey);
JSReadSubKeys(Key, SubkeyList, AFileName);
end;
end.
