rjson.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. {
  2. TRJ - JSON Simple Read and Write
  3. - v0.9.2
  4. - 2024-09-06 by gale
  5. - https://github.com/higale/RJSON
  6. }
  7. unit rjson;
  8. interface
  9. uses
  10. System.IOUtils, System.Classes, System.SysUtils, System.StrUtils, System.JSON,
  11. System.Generics.Collections;
  12. type
  13. /// <summary>Alias for <see cref="TJSONObject"/>.</summary>
  14. TJObject = TJSONObject;
  15. /// <summary>Alias for <see cref="TJSONArray"/>.</summary>
  16. TJArray = TJSONArray;
  17. /// <summary>Alias for <see cref="TJSONValue"/>.</summary>
  18. TJValue = TJSONValue;
  19. /// <summary>Alias for <see cref="TJSONString"/>.</summary>
  20. TJString = TJSONString;
  21. /// <summary>Alias for <see cref="TJSONNumber"/>.</summary>
  22. TJNumber = TJSONNumber;
  23. /// <summary>Alias for <see cref="TJSONBool"/>.</summary>
  24. TJBool = TJSONBool;
  25. /// <summary>Alias for <see cref="TJSONNull"/>.</summary>
  26. TJNull = TJSONNull;
  27. /// <summary>
  28. /// Type alias for the <see cref="TJSONValue"/> class.
  29. /// </summary>
  30. TJVType = type of TJSONValue;
  31. /// <summary>
  32. /// Interface for the root of a JSON structure.
  33. /// </summary>
  34. IRJRoot = interface
  35. function GetData: TJSONValue;
  36. procedure SetData(const AValue: TJSONValue);
  37. function ForceJV(AType: TJVType): TJSONValue;
  38. property Data: TJSONValue read GetData write SetData;
  39. end;
  40. /// <summary>
  41. /// Class representing the root of a JSON structure, implementing the <see cref="IRJRoot"/> interface.
  42. /// </summary>
  43. TRJRoot = class(TInterfacedObject, IRJRoot)
  44. private
  45. FData: TJSONValue;
  46. function GetData: TJSONValue;
  47. procedure SetData(const AValue: TJSONValue);
  48. function ForceJV(AType: TJVType): TJSONValue;
  49. public
  50. constructor Create; overload;
  51. constructor Create(const AValue: TJSONValue); overload;
  52. destructor Destroy; override;
  53. end;
  54. TRJEnumerator = class;
  55. /// <summary>
  56. /// Encapsulating common JSON data operation functionalities.<
  57. /// </summary>
  58. TRJ = record
  59. private
  60. FRoot: IRJRoot;
  61. FPath: string;
  62. function LinkPath(const ALeft, ARight: string): string;
  63. function GetJSONValue: TJSONValue; inline;
  64. function GetItems(const APath: string): TRJ; overload;
  65. function GetItems(AIndex: Integer): TRJ; overload; inline;
  66. function GetPairs(AIndex: Integer): TRJ;
  67. procedure SetValue(const [ref] AValue: TRJ);
  68. procedure SetItems(const APath: string; const [ref] AValue: TRJ); overload;
  69. procedure SetItems(AIndex: Integer; const [ref] AValue: TRJ); overload; inline;
  70. function GetCount: Integer;
  71. function GetIndex: Integer;
  72. function GetKey: string;
  73. //function GetIsNil: Boolean;
  74. public
  75. function GetEnumerator(): TRJEnumerator;
  76. class operator Initialize(out Dest: TRJ);
  77. class operator Finalize(var Dest: TRJ);
  78. class operator Assign(var Dest: TRJ; const [ref] Src: TRJ);
  79. class operator Implicit(const Value: string): TRJ;
  80. class operator Implicit(const [ref] Value: TRJ): string;
  81. class operator Implicit(Value: Integer): TRJ;
  82. class operator Implicit(const [ref] Value: TRJ): Integer;
  83. class operator Implicit(Value: Int64): TRJ;
  84. class operator Implicit(const [ref] Value: TRJ): Int64;
  85. class operator Implicit(Value: Extended): TRJ;
  86. class operator Implicit(const [ref] Value: TRJ): Extended;
  87. class operator Implicit(Value: Boolean): TRJ;
  88. class operator Implicit(const [ref] Value: TRJ): Boolean;
  89. class operator Implicit(const Value: TJSONValue): TRJ;
  90. /// <summary>Attempts to convert an object to a string representation.</summary>
  91. /// <param name="ADefault">A default value to return if the conversion fails.</param>
  92. /// <returns>The converted string value, or the specified default value if the conversion fails.</returns>
  93. function ToStr(const ADefault: string = ''): string;
  94. /// <summary>Attempts to convert an object to an integer type.</summary>
  95. /// <param name="ADefault">A default value to return if the conversion fails.</param>
  96. /// <returns>The converted integer value, or the specified default value if the conversion fails.</returns>
  97. function ToInt(ADefault: Integer = 0): Integer;
  98. /// <summary>Attempts to convert an object to a 64-bit integer type.</summary>
  99. /// <param name="ADefault">A default value to return if the conversion fails.</param>
  100. /// <returns>The converted 64-bit integer value, or the specified default value if the conversion fails.</returns>
  101. function ToInt64(ADefault: Int64 = 0): Int64;
  102. /// <summary>Attempts to convert an object to a floating-point number.</summary>
  103. /// <param name="ADefault">A default value to return if the conversion fails.</param>
  104. /// <returns>The converted floating-point value, or the specified default value if the conversion fails.</returns>
  105. function ToFloat(ADefault: Extended = 0.0): Extended;
  106. /// <summary>Attempts to convert an object to a boolean value.</summary>
  107. /// <param name="ADefault">A default value to return if the conversion fails.</param>
  108. /// <returns>The converted boolean value, or the specified default value if the conversion fails.</returns>
  109. function ToBool(ADefault: Boolean = False): Boolean;
  110. /// <summary>Gets or sets the item at the specified path.</summary>
  111. /// <param name="APath">The path of the item.</param>
  112. /// <value>The item at the specified path.</value>
  113. property Items[const APath: string]: TRJ read GetItems write SetItems; default;
  114. /// <summary>Gets or sets the item at the specified index.</summary>
  115. /// <param name="AIndex">The index of the item.</param>
  116. /// <value>The item at the specified index.</value>
  117. property Items[AIndex: Integer]: TRJ read GetItems write SetItems; default;
  118. /// <summary>Gets the pair at the specified index.</summary>
  119. /// <param name="AIndex">The index of the pair.</param>
  120. /// <value>The pair at the specified index.</value>
  121. property Pairs[AIndex: Integer]: TRJ read GetPairs;
  122. /// <summary>Gets the count of items.</summary>
  123. /// <value>The count of items.</value>
  124. property Count: Integer read GetCount;
  125. /// <summary>Gets the current index.</summary>
  126. /// <value>The current index.</value>
  127. property Index: Integer read GetIndex;
  128. /// <summary>Gets the key of the current item.</summary>
  129. /// <value>The key of the current item.</value>
  130. property Key: string read GetKey;
  131. /// <summary>Gets the root of the JSON structure.</summary>
  132. /// <value>The root of the JSON structure.</value>
  133. property Root: IRJRoot read FRoot;
  134. /// <summary>Gets the current path in the JSON structure.</summary>
  135. /// <value>The current path in the JSON structure.</value>
  136. property Path: string read FPath;
  137. /// <summary>Determines whether the root is of the specified JSON value type.</summary>
  138. /// <typeparam name="T">The type of JSON value to check against.</typeparam>
  139. /// <returns><c>true</c> if the root is of the specified type; otherwise, <c>false</c>.</returns>
  140. function RootIs<T: TJSONValue>: Boolean;
  141. /// <summary>Determines whether the value is of the specified JSON value type.</summary>
  142. /// <typeparam name="T">The type of JSON value to check against.</typeparam>
  143. /// <returns><c>true</c> if the value is of the specified type; otherwise, <c>false</c>.</returns>
  144. function ValueIs<T: TJSONValue>: Boolean;
  145. /// <summary>Gets the JSON value.</summary>
  146. /// <value>The JSON value.</value>
  147. property JSONValue: TJSONValue read GetJSONValue;
  148. /// <summary>Creates a clone of the JSON value.</summary>
  149. /// <returns>A clone of the JSON value.</returns>
  150. function CloneJSONValue: TJSONValue;
  151. /// <summary>Resets the JSON value to its initial state.</summary>
  152. procedure Reset;
  153. /// <summary>Formats the JSON value as a string with optional indentation.</summary>
  154. /// <param name="Indentation">The number of spaces to use for indentation. Defaults to 4.</param>
  155. /// <returns>The formatted JSON string.</returns>
  156. function Format(Indentation: Integer = 4): string;
  157. /// <summary>Parses the given JSON data string into a JSON value.</summary>
  158. /// <param name="AData">The JSON data string to parse.</param>
  159. /// <param name="AUseBool">Indicates whether to use boolean values for parsing. Defaults to <c>false</c>.</param>
  160. /// <param name="ARaiseExc">Indicates whether to raise an exception on parsing errors. Defaults to <c>false</c>.</param>
  161. procedure ParseJSONValue(const AData: string; AUseBool: Boolean = False; ARaiseExc: Boolean = False);
  162. /// <summary>Loads JSON data from a file and parses it into a JSON value.</summary>
  163. /// <param name="AFileName">The name of the file to load JSON data from.</param>
  164. /// <param name="AUseBool">Indicates whether to use boolean values for parsing. Defaults to <c>false</c>.</param>
  165. /// <param name="ARaiseExc">Indicates whether to raise an exception on parsing errors. Defaults to <c>false</c>.</param>
  166. procedure LoadFromFile(const AFileName: string; AUseBool: Boolean = False; ARaiseExc: Boolean = False);
  167. /// <summary>Saves the JSON value to a file with optional formatting.</summary>
  168. /// <param name="AFileName">The name of the file to save the JSON data to.</param>
  169. /// <param name="AIndentation">The number of spaces to use for indentation. Defaults to 4.</param>
  170. /// <param name="AWriteBOM">Indicates whether to write a byte order mark (BOM) at the beginning of the file. Defaults to <c>false</c>.</param>
  171. /// <param name="ATrailingLineBreak">Indicates whether to add a trailing line break at the end of the file. Defaults to <c>false</c>.</param>
  172. procedure SaveToFile(const AFileName: string; AIndentation: Integer = 4; AWriteBOM: Boolean = False; ATrailingLineBreak: Boolean = False);
  173. end;
  174. { Iterators }
  175. TRJEnumerator = class
  176. private
  177. FPData: ^TRJ;
  178. FIndex: Integer;
  179. function GetCurrent: TRJ;
  180. public
  181. constructor Create(const [ref] AData: TRJ);
  182. function MoveNext: Boolean;
  183. property Current: TRJ read GetCurrent;
  184. end;
  185. implementation
  186. { ============================================================================ }
  187. { TRJRoot }
  188. constructor TRJRoot.Create;
  189. begin
  190. inherited;
  191. FData := nil;
  192. end;
  193. constructor TRJRoot.Create(const AValue: TJSONValue);
  194. begin
  195. inherited Create;
  196. FData := AValue;
  197. end;
  198. destructor TRJRoot.Destroy;
  199. begin
  200. FData.Free;
  201. inherited;
  202. end;
  203. function TRJRoot.GetData: TJSONValue;
  204. begin
  205. Result := FData;
  206. end;
  207. procedure TRJRoot.SetData(const AValue: TJSONValue);
  208. begin
  209. FData := AValue;
  210. end;
  211. function TRJRoot.ForceJV(AType: TJVType): TJSONValue;
  212. begin
  213. if not(FData is AType) then
  214. begin
  215. FData.Free;
  216. FData := AType.Create;
  217. end;
  218. Result := FData;
  219. end;
  220. { TRJRoot }
  221. { ============================================================================ }
  222. { TJSONValueHelper }
  223. type
  224. TJSONValueHelper = class helper for TJSONValue
  225. private
  226. procedure ObjSetItem(const AName: string; const AValue: TJSONValue);
  227. procedure ArrFill<T: TJSONValue>(ACount: Integer);
  228. procedure ArrInsert(const AIndex: Integer; const AValue: TJSONValue);
  229. procedure ArrSetItem(AIndex: Integer; const AValue: TJSONValue);
  230. function ToType<T>(ADefault: T): T;
  231. function GetOrCreate<T: TJSONValue>(AName: string): T;
  232. procedure SetValue(const APath: string; const AValue: TJSONValue);
  233. end;
  234. procedure TJSONValueHelper.ObjSetItem(const AName: string; const AValue: TJSONValue);
  235. var
  236. pairTmp: TJSONPair;
  237. begin
  238. pairTmp := TJSONObject(self).Get(AName);
  239. if pairTmp = nil then
  240. TJSONObject(self).AddPair(AName, AValue)
  241. else
  242. pairTmp.JSONValue := AValue;
  243. end;
  244. procedure TJSONValueHelper.ArrFill<T>(ACount: Integer);
  245. begin
  246. for var j := TJSONArray(self).Count to ACount do
  247. TJSONArray(self).AddElement(T.Create);
  248. end;
  249. procedure TJSONValueHelper.ArrInsert(const AIndex: Integer; const AValue: TJSONValue);
  250. begin
  251. TJSONArray(self).AddElement(AValue);
  252. for var i := AIndex to TJSONArray(self).Count - 2 do
  253. TJSONArray(self).AddElement(TJSONArray(self).Remove(AIndex));
  254. end;
  255. procedure TJSONValueHelper.ArrSetItem(AIndex: Integer; const AValue: TJSONValue);
  256. begin
  257. ArrFill<TJSONNull>(AIndex - 1);
  258. if AIndex <= TJSONArray(self).Count - 1 then
  259. TJSONArray(self).Remove(AIndex).Free;
  260. ArrInsert(AIndex, AValue);
  261. end;
  262. procedure TJSONValueHelper.SetValue(const APath: string; const AValue: TJSONValue);
  263. var
  264. LParser: TJSONPathParser;
  265. preName: string;
  266. jv: TJSONValue;
  267. begin
  268. if APath.IsEmpty then
  269. raise Exception.Create('TJSONValueHelper.SetValue: path cannot be empty');
  270. jv := self;
  271. LParser := TJSONPathParser.Create(APath);
  272. LParser.NextToken;
  273. while true do
  274. begin
  275. preName := LParser.TokenName;
  276. LParser.NextToken;
  277. case LParser.Token of
  278. TJSONPathParser.TToken.Name:
  279. jv := jv.GetOrCreate<TJSONObject>(preName);
  280. TJSONPathParser.TToken.ArrayIndex:
  281. jv := jv.GetOrCreate<TJSONArray>(preName);
  282. TJSONPathParser.TToken.Eof:
  283. begin
  284. if jv is TJSONObject then
  285. jv.ObjSetItem(preName, AValue)
  286. else
  287. jv.ArrSetItem(preName.ToInteger, AValue);
  288. break;
  289. end;
  290. else
  291. raise Exception.Create('TJSONValueHelper.SetValue, LParser.Token Error!');
  292. end;
  293. end;
  294. end;
  295. function TJSONValueHelper.ToType<T>(ADefault: T): T;
  296. begin
  297. if self = nil then
  298. Exit(ADefault);
  299. try
  300. Result := AsType<T>;
  301. except
  302. Result := ADefault;
  303. end;
  304. end;
  305. function TJSONValueHelper.GetOrCreate<T>(AName: string): T;
  306. begin
  307. if self is TJSONObject then
  308. begin
  309. Result := T(TJSONObject(self).GetValue(AName));
  310. if not(Result is T) then
  311. begin
  312. Result := T.Create;
  313. ObjSetItem(AName, Result);
  314. end;
  315. end
  316. else if self is TJSONArray then
  317. begin
  318. ArrFill<TJSONNull>(AName.ToInteger);
  319. Result := T(TJSONArray(self).Items[AName.ToInteger]);
  320. if not(Result is T) then
  321. begin
  322. Result := T.Create;
  323. ArrSetItem(AName.ToInteger, Result);
  324. end;
  325. end
  326. else
  327. begin
  328. raise Exception.Create('GetOrCreate<T> Error, self must be TJO or TJA');
  329. end;
  330. end;
  331. { TJSONValueHelper }
  332. { ============================================================================ }
  333. { TRJEnumerator }
  334. constructor TRJEnumerator.Create(const [ref] AData: TRJ);
  335. begin
  336. inherited Create;
  337. FPData := @AData;
  338. FIndex := -1;
  339. end;
  340. function TRJEnumerator.GetCurrent: TRJ;
  341. var
  342. jvTmp: TJSONValue;
  343. begin
  344. Result.Reset;
  345. Result.FRoot := FPData^.FRoot;
  346. jvTmp := FPData^.GetJSONValue;
  347. if jvTmp is TJSONObject then
  348. begin
  349. if FPData^.FPath = '' then
  350. Result.FPath := TJSONObject(jvTmp).Pairs[FIndex].JsonString.Value
  351. else
  352. Result.FPath := FPData^.FPath + '.' + TJSONObject(jvTmp).Pairs[FIndex].JsonString.Value;
  353. end
  354. else if jvTmp is TJSONArray then
  355. begin
  356. Result.FPath := FPData^.FPath + '[' + FIndex.ToString + ']';
  357. end;
  358. end;
  359. function TRJEnumerator.MoveNext: Boolean;
  360. begin
  361. Inc(FIndex);
  362. Exit(FIndex < FPData^.Count)
  363. end;
  364. { TRJEnumerator }
  365. { ============================================================================ }
  366. { TRJ }
  367. function TRJ.GetEnumerator(): TRJEnumerator;
  368. begin
  369. Result := TRJEnumerator.Create(self);
  370. end;
  371. class operator TRJ.Initialize(out Dest: TRJ);
  372. begin
  373. Dest.FRoot := TRJRoot.Create;
  374. Dest.FPath := '';
  375. end;
  376. class operator TRJ.Finalize(var Dest: TRJ);
  377. begin
  378. Dest.FRoot := nil;
  379. end;
  380. function TRJ.LinkPath(const ALeft, ARight: string): string;
  381. begin
  382. if ALeft.IsEmpty then
  383. Result := ARight
  384. else if ARight.IsEmpty then
  385. Result := ALeft
  386. else if ARight.StartsWith('[') then
  387. Result := ALeft + ARight
  388. else
  389. Result := ALeft + '.' + ARight;
  390. end;
  391. function TRJ.GetJSONValue: TJSONValue;
  392. begin
  393. Result := FRoot.Data.FindValue(FPath);
  394. end;
  395. function TRJ.CloneJSONValue: TJSONValue;
  396. begin
  397. Result := GetJSONValue;
  398. if Result <> nil then
  399. Result := Result.Clone as TJSONValue
  400. else
  401. Result := TJSONNull.Create;
  402. end;
  403. class operator TRJ.Assign(var Dest: TRJ; const [ref] Src: TRJ);
  404. begin
  405. if Dest.FPath.IsEmpty then
  406. begin
  407. Dest.FRoot := Src.FRoot;
  408. Dest.FPath := Src.FPath;
  409. end
  410. else
  411. begin
  412. Dest.SetValue(Src);
  413. end;
  414. end;
  415. class operator TRJ.Implicit(const Value: string): TRJ;
  416. begin
  417. Result.FRoot.Data := TJSONString.Create(Value);
  418. end;
  419. class operator TRJ.Implicit(const [ref] Value: TRJ): string;
  420. begin
  421. Result := Value.ToStr('');
  422. end;
  423. class operator TRJ.Implicit(Value: Integer): TRJ;
  424. begin
  425. Result.FRoot.Data := TJSONNumber.Create(Value);
  426. end;
  427. class operator TRJ.Implicit(const [ref] Value: TRJ): Integer;
  428. begin
  429. Result := Value.ToInt(0);
  430. end;
  431. class operator TRJ.Implicit(Value: Int64): TRJ;
  432. begin
  433. Result.FRoot.Data := TJSONNumber.Create(Value);
  434. end;
  435. class operator TRJ.Implicit(const [ref] Value: TRJ): Int64;
  436. begin
  437. Result := Value.ToInt64(0);
  438. end;
  439. class operator TRJ.Implicit(Value: Extended): TRJ;
  440. begin
  441. Result.FRoot.Data := TJSONNumber.Create(Value);
  442. end;
  443. class operator TRJ.Implicit(const [ref] Value: TRJ): Extended;
  444. begin
  445. Result := Value.ToFloat(0.0);
  446. end;
  447. class operator TRJ.Implicit(Value: Boolean): TRJ;
  448. begin
  449. Result.FRoot.Data := TJSONBool.Create(Value);
  450. end;
  451. class operator TRJ.Implicit(const [ref] Value: TRJ): Boolean;
  452. begin
  453. Result := Value.ToBool(False);
  454. end;
  455. class operator TRJ.Implicit(const Value: TJSONValue): TRJ;
  456. begin
  457. Result.FRoot.Data := Value;
  458. end;
  459. function TRJ.ToStr(const ADefault: string): string;
  460. begin
  461. Result := FRoot.Data.FindValue(FPath).ToType<string>(ADefault);
  462. end;
  463. function TRJ.ToInt(ADefault: Integer = 0): Integer;
  464. begin
  465. Result := FRoot.Data.FindValue(FPath).ToType<Integer>(ADefault);
  466. end;
  467. function TRJ.ToInt64(ADefault: Int64 = 0): Int64;
  468. begin
  469. Result := FRoot.Data.FindValue(FPath).ToType<Int64>(ADefault);
  470. end;
  471. function TRJ.ToFloat(ADefault: Extended = 0.0): Extended;
  472. begin
  473. Result := FRoot.Data.FindValue(FPath).ToType<Extended>(ADefault);
  474. end;
  475. function TRJ.ToBool(ADefault: Boolean = False): Boolean;
  476. begin
  477. Result := FRoot.Data.FindValue(FPath).ToType<Boolean>(ADefault);
  478. end;
  479. function TRJ.GetItems(const APath: string): TRJ;
  480. begin
  481. Result.FRoot := FRoot;
  482. Result.FPath := LinkPath(FPath, APath);
  483. end;
  484. function TRJ.GetItems(AIndex: Integer): TRJ;
  485. begin
  486. Result := GetItems('[' + AIndex.ToString + ']');
  487. end;
  488. function TRJ.GetPairs(AIndex: Integer): TRJ;
  489. var
  490. jvTmp: TJSONValue;
  491. begin
  492. jvTmp := GetJSONValue;
  493. if (jvTmp is TJSONObject) then
  494. Result := GetItems(TJSONObject(jvTmp).Pairs[AIndex].JsonString.Value);
  495. end;
  496. procedure TRJ.SetValue(const [ref] AValue: TRJ);
  497. var
  498. LValue: TJSONValue;
  499. begin
  500. if FPath.IsEmpty then
  501. raise Exception.Create(' TRJ.SetValue: Path is empty');
  502. LValue := AValue.CloneJSONValue;
  503. try
  504. if FPath.StartsWith('[') then
  505. FRoot.ForceJV(TJSONArray).SetValue(FPath, LValue)
  506. else
  507. FRoot.ForceJV(TJSONObject).SetValue(FPath, LValue);
  508. except
  509. on E: Exception do
  510. begin
  511. LValue.Free;
  512. raise Exception.Create(E.message);
  513. end;
  514. end;
  515. end;
  516. procedure TRJ.SetItems(const APath: string; const [ref] AValue: TRJ);
  517. var
  518. tmp: TRJ;
  519. begin
  520. tmp.FRoot := FRoot;
  521. tmp.FPath := LinkPath(FPath, APath);
  522. tmp.SetValue(AValue)
  523. end;
  524. procedure TRJ.SetItems(AIndex: Integer; const [ref] AValue: TRJ);
  525. begin
  526. SetItems('[' + AIndex.ToString + ']', AValue);
  527. end;
  528. function TRJ.GetCount: Integer;
  529. var
  530. jvTemp: TJSONValue;
  531. begin
  532. jvTemp := GetJSONValue;
  533. if jvTemp is TJSONArray then
  534. Result := TJSONArray(jvTemp).Count
  535. else if jvTemp is TJSONObject then
  536. Result := TJSONObject(jvTemp).Count
  537. else
  538. Result := 0;
  539. end;
  540. function TRJ.GetIndex: Integer;
  541. var
  542. strTmp: string;
  543. begin
  544. Result := -1;
  545. strTmp := FPath.Substring(FPath.LastIndexOf('[') + 1);
  546. if strTmp.EndsWith(']') then
  547. Result := StrToIntDef(strTmp.TrimRight([']']), -1);
  548. end;
  549. function TRJ.GetKey: string;
  550. begin
  551. Result := FPath.Substring(FPath.LastIndexOf('.') + 1);
  552. if Result.EndsWith(']') then
  553. Result := '';
  554. end;
  555. function TRJ.RootIs<T>: Boolean;
  556. begin
  557. Result := FRoot.Data is T;
  558. end;
  559. function TRJ.ValueIs<T>: Boolean;
  560. begin
  561. Result := GetJSONValue is T;
  562. end;
  563. procedure TRJ.Reset;
  564. begin
  565. FRoot := TRJRoot.Create;
  566. FPath := '';
  567. end;
  568. function TRJ.Format(Indentation: Integer): string;
  569. var
  570. LValue: TJSONValue;
  571. begin
  572. Result := '';
  573. LValue := GetJSONValue;
  574. if LValue <> nil then
  575. begin
  576. if Indentation > 0 then
  577. Result := LValue.Format(Indentation)
  578. else
  579. Result := LValue.ToString;
  580. end;
  581. end;
  582. procedure TRJ.ParseJSONValue(const AData: string; AUseBool: Boolean; ARaiseExc: Boolean);
  583. begin
  584. Reset;
  585. FRoot.Data := TJSONValue.ParseJSONValue(AData, AUseBool, ARaiseExc);
  586. end;
  587. procedure TRJ.LoadFromFile(const AFileName: string; AUseBool: Boolean; ARaiseExc: Boolean);
  588. begin
  589. ParseJSONValue(TFile.ReadAllText(AFileName, TEncoding.UTF8), AUseBool, ARaiseExc);
  590. end;
  591. procedure TRJ.SaveToFile(const AFileName: string; AIndentation: Integer; AWriteBOM: Boolean; ATrailingLineBreak: Boolean);
  592. var
  593. strs: TStrings;
  594. begin
  595. strs := TStringList.Create;
  596. try
  597. strs.WriteBOM := AWriteBOM;
  598. strs.TrailingLineBreak := ATrailingLineBreak;
  599. strs.Text := Format(AIndentation);
  600. strs.SaveToFile(AFileName, TEncoding.UTF8);
  601. finally
  602. strs.Free;
  603. end;
  604. end;
  605. { TRJ }
  606. { ============================================================================ }
  607. end.