program JsonReadWrite2; {$mode objfpc}{$H+} uses {$IFDEF UNIX} cthreads, {$ENDIF} Classes { you can add units after this }, SysUtils, fpjson, jsonparser; type { 各アイテムのデータを保持するレコード型 } TMyRecord = record Name: string; Path: string; Args: string; end; { 配列アクセス用の型定義 } TMyRecordArray = array of TMyRecord; { 設定管理クラス } TMyConfig = class(TPersistent) private FItems: TMyRecordArray; FIndexed: integer; function GetItem(Index: integer): TMyRecord; procedure SetItem(Index: integer; const AValue: TMyRecord); public constructor Create; destructor Destroy; override; { --- JSON & ファイル操作 --- } function ToJsonString: string; procedure FromJsonString(const AJsonStr: string); procedure SaveToFile(const AFileName: string); procedure LoadFromFile(const AFileName: string); { --- データ操作メソッド (CRUD) --- } procedure AddItem(const AName, APath, AArgs: string); procedure UpdateItem(Index: integer; const AName, APath, AArgs: string); procedure RemoveItem(Index: integer); procedure ClearItems; function Count: integer; { --- 表示用プロシージャ --- } procedure PrintAll(const Msg: string); { --- プロパティ --- } property Items[Index: integer]: TMyRecord read GetItem write SetItem; default; property Indexed: integer read FIndexed write FIndexed; end; { --- TMyConfig クラスの実装 --- } constructor TMyConfig.Create; begin inherited Create; FIndexed := 0; SetLength(FItems, 0); end; destructor TMyConfig.Destroy; begin SetLength(FItems, 0); inherited Destroy; end; function TMyConfig.GetItem(Index: integer): TMyRecord; begin if (Index >= 0) and (Index < Length(FItems)) then Result := FItems[Index] else raise Exception.CreateFmt('Error: Index %d is out of bounds.', [Index]); end; procedure TMyConfig.SetItem(Index: integer; const AValue: TMyRecord); begin if (Index >= 0) and (Index < Length(FItems)) then FItems[Index] := AValue else raise Exception.CreateFmt('Error: Index %d is out of bounds.', [Index]); end; procedure TMyConfig.AddItem(const AName, APath, AArgs: string); var NewIdx: integer; begin NewIdx := Length(FItems); SetLength(FItems, NewIdx + 1); FItems[NewIdx].Name := AName; FItems[NewIdx].Path := APath; FItems[NewIdx].Args := AArgs; end; procedure TMyConfig.UpdateItem(Index: integer; const AName, APath, AArgs: string); begin if (Index >= 0) and (Index < Length(FItems)) then begin FItems[Index].Name := AName; FItems[Index].Path := APath; FItems[Index].Args := AArgs; end else raise Exception.CreateFmt('Error: Index %d is out of bounds for UpdateItem.', [Index]); end; procedure TMyConfig.RemoveItem(Index: integer); begin if (Index >= 0) and (Index < Length(FItems)) then begin Delete(FItems, Index, 1); end else raise Exception.CreateFmt('Error: Index %d is out of bounds for RemoveItem.', [Index]); end; procedure TMyConfig.ClearItems; begin SetLength(FItems, 0); end; function TMyConfig.Count: integer; begin Result := Length(FItems); end; { 一覧を出力するプロシージャ } procedure TMyConfig.PrintAll(const Msg: string); var i: integer; begin WriteLn('--- ', Msg, ' ---'); WriteLn(Format('Indexed: %d, Total Items: %d', [FIndexed, Count])); if Count = 0 then WriteLn('(No items)') else begin for i := 0 to Count - 1 do begin { Format関数を使って綺麗に整列させて出力 } WriteLn(Format('[%d] Name: %-15s | Path: %-20s | Args: %s', [i, FItems[i].Name, FItems[i].Path, FItems[i].Args])); end; end; WriteLn('----------------------------'); WriteLn; end; function TMyConfig.ToJsonString: string; var Root, ItemObj: TJSONObject; ItemsArr: TJSONArray; i: integer; begin Root := TJSONObject.Create; try Root.Add('Indexed', FIndexed); ItemsArr := TJSONArray.Create; for i := 0 to High(FItems) do begin ItemObj := TJSONObject.Create; ItemObj.Add('Name', FItems[i].Name); ItemObj.Add('Path', FItems[i].Path); ItemObj.Add('Args', FItems[i].Args); ItemsArr.Add(ItemObj); end; Root.Add('Items', ItemsArr); Result := Root.FormatJSON; finally Root.Free; end; end; procedure TMyConfig.FromJsonString(const AJsonStr: string); var JSONData: TJSONData; Root: TJSONObject; ItemsArr: TJSONArray; i: integer; begin JSONData := GetJSON(AJsonStr); try if JSONData is TJSONObject then begin Root := TJSONObject(JSONData); FIndexed := Root.Get('Indexed', 0); if Root.Find('Items', JSONData) and (JSONData is TJSONArray) then begin ItemsArr := TJSONArray(JSONData); SetLength(FItems, ItemsArr.Count); for i := 0 to ItemsArr.Count - 1 do begin FItems[i].Name := ItemsArr.Objects[i].Get('Name', ''); FItems[i].Path := ItemsArr.Objects[i].Get('Path', ''); FItems[i].Args := ItemsArr.Objects[i].Get('Args', ''); end; end; end; finally JSONData.Free; end; end; procedure TMyConfig.SaveToFile(const AFileName: string); var L: TStringList; begin L := TStringList.Create; try L.Text := ToJsonString; L.SaveToFile(AFileName); finally L.Free; end; end; procedure TMyConfig.LoadFromFile(const AFileName: string); var L: TStringList; begin if not FileExists(AFileName) then exit; L := TStringList.Create; try L.LoadFromFile(AFileName); FromJsonString(L.Text); finally L.Free; end; end; { --- メインプログラム --- } var Config: TMyConfig; FileName: string = 'settings.json'; begin Config := TMyConfig.Create; try { 1. 初期データの追加テスト } Config.AddItem('Lazarus', '/usr/bin/lazarus', '--version'); Config.AddItem('Calculator', 'calc.exe', ''); Config.AddItem('Notepad', 'notepad.exe', 'readme.txt'); Config.Indexed := 100; Config.PrintAll('After Adding Items'); { 2. 更新テスト } Config.UpdateItem(0, 'Lazarus IDE', '/opt/lazarus', '--debug'); Config.PrintAll('After Updating Item[0]'); { 3. 削除テスト } Config.RemoveItem(1); // Calculator を削除 Config.PrintAll('After Removing Item[1]'); { 4. ファイル保存と読み込みテスト } WriteLn('Saving to file: ', FileName); Config.SaveToFile(FileName); Config.ClearItems; Config.PrintAll('After Clear (Memory Empty)'); WriteLn('Loading from file...'); Config.LoadFromFile(FileName); Config.PrintAll('After Loading from File'); except on E: Exception do WriteLn('Error: ', E.Message); end; Config.Free; WriteLn('Press Enter to quit.'); ReadLn; end.