higale před 4 měsíci
rodič
revize
b313203ab7
4 změnil soubory, kde provedl 1492 přidání a 1184 odebrání
  1. 1 0
      .gitignore
  2. 150 151
      README.md
  3. 141 142
      README_zh.md
  4. 1200 891
      rjson.pas

+ 1 - 0
.gitignore

@@ -79,3 +79,4 @@ __recovery/
 
 # Boss dependency manager vendor folder https://github.com/HashLoad/boss
 modules/
+.DS_Store

+ 150 - 151
README.md

@@ -1,151 +1,150 @@
-# TRJSON - JSON Simple Read and Write
-- by gale
-- https://github.com/higale/RJSON
-
-**Languages: [English](README.md) | [中文](README_zh.md)**
-
-## Type Aliases
-- TJObject = TJSONObject;
-- TJArray = TJSONArray;
-- TJValue = TJSONValue;
-- TJString = TJSONString;
-- TJNumber = TJSONNumber;
-- TJBool = TJSONBool;
-- TJNull = TJSONNull;
-
-## Properties:
-- `Items[Path]` Default property, Reads and writes based on path, applicable to objects and arrays. Path can be a string or integer index.
-- `S[Path]` Reads and writes string values.
-- `I[Path]` Reads and writes integer values.
-- `I64[Path]` Reads and writes 64-bit integer values.
-- `F[Path]` Reads and writes floating-point values using the Extended type.
-- `B[Path]` Reads and writes boolean values.
-- `Pairs[Index]` Retrieves key-value pairs under a JSONObject.
-- `Count` The number of entries in an object or array; returns 0 for other types.
-- `Index` The index of an entry in an array; returns -1 if not array data.
-- `Key` Returns the key if it is key-value pair data; otherwise, returns an empty string.
-- `RootRefCount` Reference count of the root interface, mainly used for debugging.
-- `Root` Root data.
-- `Path` Path of the value.
-- `JValue` Contains the JSON object.
-
-## Methods
-- `ToStr` Converts to a string; defaults to an empty string.
-- `ToInt` Converts to an integer; defaults to 0.
-- `ToInt64` Converts to a 64-bit integer; defaults to 0.
-- `ToFloat` Converts to a floating-point number (using Extended); defaults to 0.0.
-- `ToBool` Converts to a boolean; defaults to False.
-
-- `CloneJValue` Clones the current value; generates TJNull if the current value does not exist.
-- `IsRoot` Checks if it is root data (Path is empty).
-- `RootIsJObject` Checks if the root is a JObject.
-- `RootIsJArray` Checks if the root is a JArray.
-- `IsJObject` Checks if the value is a JObject.
-- `IsJArray` Checks if the value is a JArray.
-- `IsJString` Checks if the value is a JString.
-- `IsJNumber` Checks if the value is a JNumber.
-- `IsJBool` Checks if the value is a JBool.
-- `IsJNull` Checks if the value is a JNull.
-- `IsNil` Checks if the value is nil.
-- `Reset` Resets to factory settings.
-
-- `ToString` Outputs a JSON string in compact format without encoding.
-- `ToJSON` Outputs a JSON string in compact format, encoding characters less than 32 or greater than 127 based on parameters.
-- `Format` Outputs a formatted JSON string.
-- `ParseJSONValue` Loads data from a string.
-- `LoadFromFile` Loads data from a file.
-- `SaveToFile` Save JSON data to a file.
-
-## Example:
-```pascal
-uses rjson;
-
-procedure TFormMain.btnTestClick(Sender: TObject);
-var
-  RJ, RJ1: TRJSON;
-  fTemp: Extended;
-begin
-  Memo1.Lines.Clear;
-  RJ['title1'] := 'hello world!';
-  RJ.S['title2'] := '世界,你好!';
-  RJ.Items['title3'] := 'Good';
-  RJ['a.num'] := 1;
-  RJ['a.haha'] := false;
-  RJ['b[2]'] := 505;
-  RJ['b[0]'] := 'first';
-  RJ['good'] := True;
-
-  RJ1 := RJ['c'];
-  RJ1['c1'] := 1.1;
-  RJ1['c2[2]'] := 2.33;
-
-  with RJ['x'] do
-  begin
-    Items[1] := 100;
-    Items[2] := '202';
-  end;
-
-  with RJ['y'] do
-  begin
-    Items['ya'] := 'y1';
-    Items['yb'] := -2;
-  end;
-
-  Memo1.Lines.Add('ToString:');
-  Memo1.Lines.Add(RJ.ToString);
-  Memo1.Lines.Add('ToJSON:');
-  Memo1.Lines.Add(RJ.ToJSON);
-  Memo1.Lines.Add('Format:');
-  Memo1.Lines.Add(RJ.Format);
-
-  Memo1.Lines.Add('-----------------------------------------------------------');
-
-  fTemp := RJ['c.c2[3]'];
-  Memo1.Lines.Add('fTemp = ' + fTemp.ToString);
-
-  fTemp := RJ['c.none[3]'].ToFloat(-100);
-  Memo1.Lines.Add('fTemp:' + fTemp.ToString);
-
-  Memo1.Lines.Add(RJ['a.num'].ToStr('a.num not exist'));
-  Memo1.Lines.Add(RJ['none'].ToFloat(-1).ToString);
-  Memo1.Lines.Add(RJ['none.a3'].ToStr('none.a3 not exist'));
-  RJ.SaveToFile('test.json', 0);
-end;
-
-procedure TFormMain.btnOpenClick(Sender: TObject);
-var
-  RJ: TRJSON;
-  strTmp: string;
-begin
-  RJ.LoadFromFile('test.json');
-  Memo1.Text := RJ.Format(8);
-  Memo1.Lines.Add('-----------------------ROOT--------------------------');
-  for var item in RJ do
-  begin
-    strTmp := Format('Index: %d  Key: %s  Value: %s',
-      [item.Index, item.Key, item.Format(0)]);
-    Memo1.Lines.Add(strTmp);
-  end;
-  Memo1.Lines.Add('-----------------------RJ[a]--------------------------');
-  for var item in RJ['a'] do
-  begin
-    strTmp := Format('Index: %d  Key: %s  Value: %s',
-      [item.Index, item.Key, item.Format(0)]);
-    Memo1.Lines.Add(strTmp);
-  end;
-  Memo1.Lines.Add('-----------------------RJ[b]--------------------------');
-  for var item in RJ['b'] do
-  begin
-    strTmp := Format('Index: %d  Key: %s  Value: %s',
-      [item.Index, item.Key, item.Format(0)]);
-    Memo1.Lines.Add(strTmp);
-  end;
-  Memo1.Lines.Add('-----------------------RJ[c]--------------------------');
-  for var i := 0 to RJ['c'].Count - 1 do
-  begin
-    strTmp := Format('Index: %d  Key: %s  Value: %s',
-      [RJ['c'].Pairs[i].Index, RJ['c'].Pairs[i].Key, RJ['c'].Pairs[i].Format(0)]);
-    Memo1.Lines.Add(strTmp);
-  end;
-end;
-```
+# TRJSON - JSON Simple Read and Write
+- by gale
+- https://github.com/higale/RJSON
+
+**Languages: [English](README.md) | [中文](README_zh.md)**
+
+## Type Aliases
+- TJObject = TJSONObject;
+- TJArray = TJSONArray;
+- TJValue = TJSONValue;
+- TJString = TJSONString;
+- TJNumber = TJSONNumber;
+- TJBool = TJSONBool;
+- TJNull = TJSONNull;
+
+## Properties:
+- `Items[Path]` Default property, Reads and writes based on path, applicable to objects and arrays. Path can be a string or integer index.
+- `S[Path]` Reads and writes string values.
+- `I[Path]` Reads and writes integer values.
+- `I64[Path]` Reads and writes 64-bit integer values.
+- `F[Path]` Reads and writes floating-point values using the Extended type.
+- `B[Path]` Reads and writes boolean values.
+- `Pairs[Index]` Retrieves key-value pairs under a JSONObject.
+- `Count` The number of entries in an object or array; returns 0 for other types.
+- `Index` The index of an entry in an array; returns -1 if not array data.
+- `Key` Returns the key if it is key-value pair data; otherwise, returns an empty string.
+- `RootRefCount` Reference count of the root interface, mainly used for debugging.
+- `Root` Root data.
+- `Path` Path of the value.
+- `JValue` Contains the JSON object.
+
+## Methods
+- `ToStr` Converts to a string; defaults to an empty string.
+- `ToInt` Converts to an integer; defaults to 0.
+- `ToInt64` Converts to a 64-bit integer; defaults to 0.
+- `ToFloat` Converts to a floating-point number (using Extended); defaults to 0.0.
+- `ToBool` Converts to a boolean; defaults to False.
+
+- `CloneJValue` Clones the current value.
+- `IsRoot` Checks if it is root data (Path is empty).
+- `RootIsObject` Checks if the root is a Object.
+- `RootIsArray` Checks if the root is a Array.
+- `IsObject` Checks if the value is a Object.
+- `IsArray` Checks if the value is a Array.
+- `IsString` Checks if the value is a String.
+- `IsNumber` Checks if the value is a Number.
+- `IsBool` Checks if the value is a Bool.
+- `IsNull` Checks if the value is a Null.
+- `IsNil` Checks if the value is nil.
+- `Reset` Resets to factory settings.
+
+- `ToJSON` Outputs a compact JSON string.
+- `Format` Outputs a formatted JSON string.
+- `ParseJSONValue` Loads data from a string.
+- `LoadFromFile` Loads data from a file.
+- `SaveToFile` Save JSON data to a file.
+
+## Example:
+```pascal
+uses rjson;
+
+procedure TFormMain.btnTestClick(Sender: TObject);
+var
+  RJ, RJ1: TRJSON;
+  fTemp: Extended;
+begin
+  Memo1.Lines.Clear;
+  RJ['title1'] := 'hello world!';
+  RJ.S['title2'] := '世界,你好!';
+  RJ.Items['title3'] := 'Good';
+  RJ['a.num'] := 1;
+  RJ['a.haha'] := false;
+  RJ['b[2]'] := 505;
+  RJ['b[0]'] := 'first';
+  RJ['good'] := True;
+
+  RJ1 := RJ['c'];
+  RJ1['c1'] := 1.1;
+  RJ1['c2[2]'] := 2.33;
+
+  with RJ['x'] do
+  begin
+    Items[1] := 100;
+    Items[2] := '202';
+  end;
+
+  with RJ['y'] do
+  begin
+    Items['ya'] := 'y1';
+    Items['yb'] := -2;
+  end;
+
+  Memo1.Lines.Add('ToString:');
+  Memo1.Lines.Add(RJ.ToString);
+  Memo1.Lines.Add('ToJSON:');
+  Memo1.Lines.Add(RJ.ToJSON);
+  Memo1.Lines.Add('Format:');
+  Memo1.Lines.Add(RJ.Format);
+
+  Memo1.Lines.Add('-----------------------------------------------------------');
+
+  fTemp := RJ['c.c2[3]'];
+  Memo1.Lines.Add('fTemp = ' + fTemp.ToString);
+
+  fTemp := RJ['c.none[3]'].ToFloat(-100);
+  Memo1.Lines.Add('fTemp:' + fTemp.ToString);
+
+  Memo1.Lines.Add(RJ['a.num'].ToStr('a.num not exist'));
+  Memo1.Lines.Add(RJ['none'].ToFloat(-1).ToString);
+  Memo1.Lines.Add(RJ['none.a3'].ToStr('none.a3 not exist'));
+  RJ.SaveToFile('test.json', 0);
+end;
+
+procedure TFormMain.btnOpenClick(Sender: TObject);
+var
+  RJ: TRJSON;
+  strTmp: string;
+begin
+  RJ.LoadFromFile('test.json');
+  Memo1.Text := RJ.Format(8);
+  Memo1.Lines.Add('-----------------------ROOT--------------------------');
+  for var item in RJ do
+  begin
+    strTmp := Format('Index: %d  Key: %s  Value: %s',
+      [item.Index, item.Key, item.Format(0)]);
+    Memo1.Lines.Add(strTmp);
+  end;
+  Memo1.Lines.Add('-----------------------RJ[a]--------------------------');
+  for var item in RJ['a'] do
+  begin
+    strTmp := Format('Index: %d  Key: %s  Value: %s',
+      [item.Index, item.Key, item.Format(0)]);
+    Memo1.Lines.Add(strTmp);
+  end;
+  Memo1.Lines.Add('-----------------------RJ[b]--------------------------');
+  for var item in RJ['b'] do
+  begin
+    strTmp := Format('Index: %d  Key: %s  Value: %s',
+      [item.Index, item.Key, item.Format(0)]);
+    Memo1.Lines.Add(strTmp);
+  end;
+  Memo1.Lines.Add('-----------------------RJ[c]--------------------------');
+  for var i := 0 to RJ['c'].Count - 1 do
+  begin
+    strTmp := Format('Index: %d  Key: %s  Value: %s',
+      [RJ['c'].Pairs[i].Index, RJ['c'].Pairs[i].Key, RJ['c'].Pairs[i].Format(0)]);
+    Memo1.Lines.Add(strTmp);
+  end;
+end;
+```

+ 141 - 142
README_zh.md

@@ -1,142 +1,141 @@
-# TRJSON - JSON Simple Read and Write
-- by gale
-- https://github.com/higale/RJSON
-
-**Languages: [English](README.md) | [中文](README_zh.md)**
-
-## 类型别名
-- TJObject = TJSONObject;
-- TJArray = TJSONArray;
-- TJValue = TJSONValue;
-- TJString = TJSONString;
-- TJNumber = TJSONNumber;
-- TJBool = TJSONBool;
-- TJNull = TJSONNull;
-
-## 属性:
-- `Items[Path]` 缺省属性,基于路径读写,适用于对象和数组,Path 可以是字符串或整数索引。
-- `S[Path]` 字符串数值读写
-- `I[Path]` 整数数值读写
-- `I64[Path]` 64位整数数值读写
-- `F[Path]` 浮点数数值读写,使用 Extended 类型
-- `B[Path]` 布尔数值读写
-- `Pairs[Index]` 获取 JSONObject 下的键值对。
-- `Count` 对象或数组中的条目数量,其他类型返回 0。
-- `Index` 数组中的条目索引,如果不是数组数据则返回 -1。
-- `Key` 如果是键值对数据,则返回键,否则返回空字符串。
-- `RootRefCount` Root接口引用次数,主要用于调试
-- `Root` 根数据。
-- `Path` 值的路径。
-- `JValue` 包含的JSON对象。
-
-## 方法
-- `ToStr` 转换为字符串,默认为空字符串。
-- `ToInt` 转换为整数,默认为 0。
-- `ToInt64` 转换为 64 位整数,默认为 0。
-- `ToFloat` 转换为浮点数(使用 Extended),默认为 0.0。
-- `ToBool` 转换为布尔值,默认为 False。
-
-- `CloneJValue` 克隆当前值,如果当前值不存在,则生成 TJNull。
-- `IsRoot` 是否是根数据(Path为空)
-- `RootIsJObject` 根是否是JObject
-- `RootIsJArray` 根是否是JArray
-- `IsJObject` 值是否是JObject
-- `IsJArray` 值是否是JObject
-- `IsJString` 值是否是JString
-- `IsJNumber` 值是否是JNumber
-- `IsJBool` 值是否是JBool
-- `IsJNull` 值是否是JJNull
-- `IsNil` 值是否为空
-- `Reset` 重置为出厂设置
-
-- `ToString` 输出 JSON 字符串,紧凑格式,不做编码。
-- `ToJSON` 输出 JSON 字符串,紧凑格式,根据参数对小于32或大于127的字符编码。
-- `Format` 输出格式化的 JSON 字符串。
-- `ParseJSONValue` 从字符串加载数据。
-- `LoadFromFile` 从文件加载数据。
-- `SaveToFile` 将JSON数据保存到文件。
-
-## 示例:
-```pascal
-uses rjson;
-
-procedure TFormMain.btnTestClick(Sender: TObject);
-var
-  RJ, RJ1: TRJSON;
-  fTemp: Extended;
-begin
-  Memo1.Lines.Clear;
-  RJ['title1'] := 'hello world!';
-  RJ.S['title2'] := '世界,你好!';
-  RJ.Items['title3'] := 'Good';
-  RJ['a.num'] := 1;
-  RJ['a.haha'] := false;
-  RJ['b[2]'] := 505;
-  RJ['b[0]'] := 'first';
-  RJ['good'] := True;
-
-  RJ1 := RJ['c'];
-  RJ1['c1'] := 1.1;
-  RJ1['c2[2]'] := 2.33;
-
-  with RJ['x'] do
-  begin
-    items[1] := 100;
-    items[2] := '202';
-  end;
-
-  with RJ['y'] do
-  begin
-    items['ya'] := 'y1';
-    items['yb'] := -2;
-  end;
-
-  Memo1.Text := RJ.Format;
-  Memo1.Lines.Add('-----------------------------------------------------------');
-  fTemp := RJ['c.c2[3]'];
-  Memo1.Lines.Add('fTemp:' + fTemp.ToString);
-  fTemp := RJ['c.c3[3]'].ToFloat(-100);
-  Memo1.Lines.Add('fTemp:' + fTemp.ToString);
-  Memo1.Lines.Add(RJ['a.num'].ToStr('a.num not exist'));
-  Memo1.Lines.Add(RJ['none'].ToFloat(-1).ToString);
-  Memo1.Lines.Add(RJ['none.a3'].ToStr('none.a3 not exist'));
-  RJ.SaveToFile('test.json', 0);
-end;
-
-procedure TFormMain.btnOpenClick(Sender: TObject);
-var
-  RJ: TRJSON;
-  strTmp: string;
-begin
-  RJ.LoadFromFile('test.json');
-  Memo1.Text := RJ.Format(8);
-  Memo1.Lines.Add('-----------------------ROOT--------------------------');
-  for var item in RJ do
-  begin
-    strTmp := Format('Index: %d  Key: %s  Value: %s',
-      [item.Index, item.Key, item.Format(0)]);
-    Memo1.Lines.Add(strTmp);
-  end;
-  Memo1.Lines.Add('-----------------------RJ[a]--------------------------');
-  for var item in RJ['a'] do
-  begin
-    strTmp := Format('Index: %d  Key: %s  Value: %s',
-      [item.Index, item.Key, item.Format(0)]);
-    Memo1.Lines.Add(strTmp);
-  end;
-  Memo1.Lines.Add('-----------------------RJ[b]--------------------------');
-  for var item in RJ['b'] do
-  begin
-    strTmp := Format('Index: %d  Key: %s  Value: %s',
-      [item.Index, item.Key, item.Format(0)]);
-    Memo1.Lines.Add(strTmp);
-  end;
-  Memo1.Lines.Add('-----------------------RJ[c]--------------------------');
-  for var i := 0 to RJ['c'].Count - 1 do
-  begin
-    strTmp := Format('Index: %d  Key: %s  Value: %s',
-      [RJ['c'].Pairs[i].Index, RJ['c'].Pairs[i].Key, RJ['c'].Pairs[i].Format(0)]);
-    Memo1.Lines.Add(strTmp);
-  end;
-end;
-```
+# TRJSON - JSON Simple Read and Write
+- by gale
+- https://github.com/higale/RJSON
+
+**Languages: [English](README.md) | [中文](README_zh.md)**
+
+## 类型别名
+- TJObject = TJSONObject;
+- TJArray = TJSONArray;
+- TJValue = TJSONValue;
+- TJString = TJSONString;
+- TJNumber = TJSONNumber;
+- TJBool = TJSONBool;
+- TJNull = TJSONNull;
+
+## 属性:
+- `Items[Path]` 缺省属性,基于路径读写,适用于对象和数组,Path 可以是字符串或整数索引。
+- `S[Path]` 字符串数值读写
+- `I[Path]` 整数数值读写
+- `I64[Path]` 64位整数数值读写
+- `F[Path]` 浮点数数值读写,使用 Extended 类型
+- `B[Path]` 布尔数值读写
+- `Pairs[Index]` 获取 JSONObject 下的键值对。
+- `Count` 对象或数组中的条目数量,其他类型返回 0。
+- `Index` 数组中的条目索引,如果不是数组数据则返回 -1。
+- `Key` 如果是键值对数据,则返回键,否则返回空字符串。
+- `RootRefCount` Root接口引用次数,主要用于调试
+- `Root` 根数据。
+- `Path` 值的路径。
+- `JValue` 包含的JSON对象。
+
+## 方法
+- `ToStr` 转换为字符串,默认为空字符串。
+- `ToInt` 转换为整数,默认为 0。
+- `ToInt64` 转换为 64 位整数,默认为 0。
+- `ToFloat` 转换为浮点数(使用 Extended),默认为 0.0。
+- `ToBool` 转换为布尔值,默认为 False。
+
+- `CloneJValue` 克隆当前值。
+- `IsRoot` 是否是根数据(Path为空)。
+- `RootIsObject` 根是否是Object。
+- `RootIsArray` 根是否是Array。
+- `IsObject` 值是否是Object。
+- `IsArray` 值是否是Array。
+- `IsString` 值是否是String。
+- `IsNumber` 值是否是Number。
+- `IsBool` 值是否是Bool。
+- `IsNull` 值是否是JNull。
+- `IsNil` 值是否为空。
+- `Reset` 重置为出厂设置。
+
+- `ToJSON` 输出紧凑格式 JSON 字符串。
+- `Format` 输出格式化的 JSON 字符串。
+- `ParseJSONValue` 从字符串加载数据。
+- `LoadFromFile` 从文件加载数据。
+- `SaveToFile` 将JSON数据保存到文件。
+
+## 示例:
+```pascal
+uses rjson;
+
+procedure TFormMain.btnTestClick(Sender: TObject);
+var
+  RJ, RJ1: TRJSON;
+  fTemp: Extended;
+begin
+  Memo1.Lines.Clear;
+  RJ['title1'] := 'hello world!';
+  RJ.S['title2'] := '世界,你好!';
+  RJ.Items['title3'] := 'Good';
+  RJ['a.num'] := 1;
+  RJ['a.haha'] := false;
+  RJ['b[2]'] := 505;
+  RJ['b[0]'] := 'first';
+  RJ['good'] := True;
+
+  RJ1 := RJ['c'];
+  RJ1['c1'] := 1.1;
+  RJ1['c2[2]'] := 2.33;
+
+  with RJ['x'] do
+  begin
+    items[1] := 100;
+    items[2] := '202';
+  end;
+
+  with RJ['y'] do
+  begin
+    items['ya'] := 'y1';
+    items['yb'] := -2;
+  end;
+
+  Memo1.Text := RJ.Format;
+  Memo1.Lines.Add('-----------------------------------------------------------');
+  fTemp := RJ['c.c2[3]'];
+  Memo1.Lines.Add('fTemp:' + fTemp.ToString);
+  fTemp := RJ['c.c3[3]'].ToFloat(-100);
+  Memo1.Lines.Add('fTemp:' + fTemp.ToString);
+  Memo1.Lines.Add(RJ['a.num'].ToStr('a.num not exist'));
+  Memo1.Lines.Add(RJ['none'].ToFloat(-1).ToString);
+  Memo1.Lines.Add(RJ['none.a3'].ToStr('none.a3 not exist'));
+  RJ.SaveToFile('test.json', 0);
+end;
+
+procedure TFormMain.btnOpenClick(Sender: TObject);
+var
+  RJ: TRJSON;
+  strTmp: string;
+begin
+  RJ.LoadFromFile('test.json');
+  Memo1.Text := RJ.Format(8);
+  Memo1.Lines.Add('-----------------------ROOT--------------------------');
+  for var item in RJ do
+  begin
+    strTmp := Format('Index: %d  Key: %s  Value: %s',
+      [item.Index, item.Key, item.Format(0)]);
+    Memo1.Lines.Add(strTmp);
+  end;
+  Memo1.Lines.Add('-----------------------RJ[a]--------------------------');
+  for var item in RJ['a'] do
+  begin
+    strTmp := Format('Index: %d  Key: %s  Value: %s',
+      [item.Index, item.Key, item.Format(0)]);
+    Memo1.Lines.Add(strTmp);
+  end;
+  Memo1.Lines.Add('-----------------------RJ[b]--------------------------');
+  for var item in RJ['b'] do
+  begin
+    strTmp := Format('Index: %d  Key: %s  Value: %s',
+      [item.Index, item.Key, item.Format(0)]);
+    Memo1.Lines.Add(strTmp);
+  end;
+  Memo1.Lines.Add('-----------------------RJ[c]--------------------------');
+  for var i := 0 to RJ['c'].Count - 1 do
+  begin
+    strTmp := Format('Index: %d  Key: %s  Value: %s',
+      [RJ['c'].Pairs[i].Index, RJ['c'].Pairs[i].Key, RJ['c'].Pairs[i].Format(0)]);
+    Memo1.Lines.Add(strTmp);
+  end;
+end;
+```

+ 1200 - 891
rjson.pas

@@ -1,891 +1,1200 @@
-{
-  TRJSON - JSON Simple Read and Write
-  - v0.9.9
-  - 2024-09-17 by gale
-  - https://github.com/higale/RJSON
-}
-unit rjson;
-
-interface
-
-uses
-  System.IOUtils, System.Classes, System.SysUtils, System.JSON, System.Generics.Collections;
-
-type
-  TJObject = TJSONObject;
-  TJArray = TJSONArray;
-  TJValue = TJSONValue;
-  TJString = TJSONString;
-  TJNumber = TJSONNumber;
-  TJBool = TJSONBool;
-  TJTrue = TJSONTrue;
-  TJFalse = TJSONFalse;
-  TJNull = TJSONNull;
-  TJVType = type of TJValue;
-
-  IRJRoot = interface
-    ['{486F1FA6-2CDD-4124-98C5-CE7C398B7143}']
-    function GetData: TJValue;
-    procedure SetData(const AValue: TJValue);
-    function ForceData(AType: TJVType): TJValue;
-    property Data: TJValue read GetData write SetData;
-  end;
-
-  TRJSONRoot = class(TInterfacedObject, IRJRoot)
-  private
-    FData: TJValue;
-    function GetData: TJValue;
-    procedure SetData(const AValue: TJValue);
-    function ForceData(AType: TJVType): TJValue;
-  public
-    constructor Create;
-    destructor Destroy; override;
-  end;
-
-  TRPath = record
-  private
-    FData: string;
-  public
-    class operator Implicit(const Value: string): TRPath;
-    class operator Implicit(Value: Integer): TRPath;
-    class operator Implicit(const [ref] Value: TRPath): string;
-  end;
-
-  TRJSONEnumerator = class;
-
-  TRJSON = record
-  private
-    FIRoot: IRJRoot;
-    FPath: string;
-    function GetRootRefCount: Integer;
-    function ForceRootJValue(const APath: string): TJValue;
-    function LinkPath(const ALeft, ARight: string): string;
-    function GetJValue: TJValue;
-    function GetItems(const APath: TRPath): TRJSON;
-    function GetPairs(AIndex: Integer): TRJSON;
-    procedure SetValue(const [ref] AValue: TRJSON);
-    procedure SetItems(const APath: TRPath; const [ref] AValue: TRJSON);
-    function GetS(const APath: TRPath): string; overload;
-    procedure SetS(const APath: TRPath; AValue: string); overload;
-    function GetI(const APath: TRPath): Integer; overload;
-    procedure SetI(const APath: TRPath; AValue: Integer); overload;
-    function GetI64(const APath: TRPath): Int64; overload;
-    procedure SetI64(const APath: TRPath; AValue: Int64); overload;
-    function GetF(const APath: TRPath): Extended; overload;
-    procedure SetF(const APath: TRPath; AValue: Extended); overload;
-    function GetB(const APath: TRPath): Boolean; overload;
-    procedure SetB(const APath: TRPath; AValue: Boolean); overload;
-    function GetCount: Integer;
-    function GetLastPath: string;
-    function GetIndex: Integer;
-    function GetKey: string;
-    function GetRoot: TRJSON;
-  public
-    function GetEnumerator(): TRJSONEnumerator;
-    class operator Initialize(out Dest: TRJSON);
-    class operator Finalize(var Dest: TRJSON);
-    class operator Assign(var Dest: TRJSON; const [ref] Src: TRJSON);
-    class operator Implicit(const Value: string): TRJSON;
-    class operator Implicit(const [ref] Value: TRJSON): string;
-    class operator Implicit(Value: Integer): TRJSON;
-    class operator Implicit(const [ref] Value: TRJSON): Integer;
-    class operator Implicit(Value: Int64): TRJSON;
-    class operator Implicit(const [ref] Value: TRJSON): Int64;
-    class operator Implicit(Value: Extended): TRJSON;
-    class operator Implicit(const [ref] Value: TRJSON): Extended;
-    class operator Implicit(Value: Boolean): TRJSON;
-    class operator Implicit(const [ref] Value: TRJSON): Boolean;
-    class operator Implicit(const Value: TJValue): TRJSON;
-    class operator Implicit(const [ref] Value: TRJSON): TJValue;
-    function ToStr(const ADefault: string = ''): string;
-    function ToInt(ADefault: Integer = 0): Integer;
-    function ToInt64(ADefault: Int64 = 0): Int64;
-    function ToFloat(ADefault: Extended = 0.0): Extended;
-    function ToBool(ADefault: Boolean = False): Boolean;
-
-    property Items[const APath: TRPath]: TRJSON read GetItems write SetItems; default;
-    property S[const APath: TRPath]: string read GetS write SetS;
-    property I[const APath: TRPath]: Integer read GetI write SetI;
-    property I64[const APath: TRPath]: Int64 read GetI64 write SetI64;
-    property F[const APath: TRPath]: Extended read GetF write SetF;
-    property B[const APath: TRPath]: Boolean read GetB write SetB;
-    property Pairs[AIndex: Integer]: TRJSON read GetPairs;
-    property Count: Integer read GetCount;
-    property LastPath: string read GetLastPath;
-    property Index: Integer read GetIndex;
-    property Key: string read GetKey;
-    property RootRefCount: Integer read GetRootRefCount;
-    property Root: TRJSON read GetRoot;
-    property Path: string read FPath;
-    property JValue: TJValue read GetJValue;
-
-    function CloneJValue: TJValue;
-    function IsRoot: Boolean; inline;
-    function RootIsJObject: Boolean; inline;
-    function RootIsJArray: Boolean; inline;
-    function IsJObject: Boolean;
-    function IsJArray: Boolean;
-    function IsJString: Boolean;
-    function IsJNumber: Boolean;
-    function IsJBool: Boolean;
-    function IsJNull: Boolean;
-    function IsNil: Boolean;
-    procedure Reset;
-    function ToString: string;
-    function ToJSON(AEncodeBelow32: Boolean = true; AEncodeAbove127: Boolean = true): string;
-    function Format(AIndentation: Integer = 4; AEncodeBelow32: Boolean = False; AEncodeAbove127: Boolean = False): string;
-    function ParseJValue(const AData: string; AUseBool: Boolean = False; ARaiseExc: Boolean = False): Boolean;
-    function LoadFromFile(const AFileName: string; AUseBool: Boolean = False; ARaiseExc: Boolean = False): Boolean;
-    procedure SaveToFile(const AFileName: string; AIndentation: Integer = -1; AEncodeBelow32: Boolean = true; AEncodeAbove127: Boolean = true; AWriteBOM: Boolean = False);
-  end;
-
-  { Iterators }
-  TRJSONEnumerator = class
-  private
-    FPData: ^TRJSON;
-    FIndex: Integer;
-    function GetCurrent: TRJSON;
-  public
-    constructor Create(const [ref] AData: TRJSON);
-    function MoveNext: Boolean;
-    property Current: TRJSON read GetCurrent;
-  end;
-
-implementation
-
-{ ============================================================================ }
-{ TRJSONRoot }
-
-constructor TRJSONRoot.Create;
-begin
-  inherited;
-  FData := nil;
-end;
-
-destructor TRJSONRoot.Destroy;
-begin
-  FData.Free;
-  inherited;
-end;
-
-function TRJSONRoot.GetData: TJValue;
-begin
-  Result := FData;
-end;
-
-procedure TRJSONRoot.SetData(const AValue: TJValue);
-begin
-  FData := AValue;
-end;
-
-function TRJSONRoot.ForceData(AType: TJVType): TJValue;
-begin
-  if not(FData is AType) then
-  begin
-    FData.Free;
-    FData := AType.Create;
-  end;
-  Result := FData;
-end;
-
-{ TRJSONRoot }
-{ ============================================================================ }
-{ TJValueHelper }
-
-type
-  TJValueHelper = class helper for TJValue
-  private
-    procedure ObjSetItem(const AName: string; const AValue: TJValue);
-    procedure ArrFill<T: TJValue>(ACount: Integer);
-    procedure ArrInsert(const AIndex: Integer; const AValue: TJValue);
-    procedure ArrSetItem(AIndex: Integer; const AValue: TJValue);
-    function ToType<T>(ADefault: T): T;
-    function GetOrCreate<T: TJValue>(AName: string): T;
-    procedure SetValue(const APath: string; const AValue: TJValue);
-    procedure TrySetValue(const APath: string; const AValue: TJValue);
-  end;
-
-procedure TJValueHelper.ObjSetItem(const AName: string; const AValue: TJValue);
-var
-  pairTmp: TJSONPair;
-begin
-  pairTmp := TJObject(self).Get(AName);
-  if pairTmp = nil then
-    TJObject(self).AddPair(AName, AValue)
-  else
-    pairTmp.JSONValue := AValue;
-end;
-
-procedure TJValueHelper.ArrFill<T>(ACount: Integer);
-begin
-  for var j := TJArray(self).Count to ACount do
-    TJArray(self).AddElement(T.Create);
-end;
-
-procedure TJValueHelper.ArrInsert(const AIndex: Integer; const AValue: TJValue);
-begin
-  TJArray(self).AddElement(AValue);
-  for var I := AIndex to TJArray(self).Count - 2 do
-    TJArray(self).AddElement(TJArray(self).Remove(AIndex));
-end;
-
-procedure TJValueHelper.ArrSetItem(AIndex: Integer; const AValue: TJValue);
-begin
-  ArrFill<TJNull>(AIndex - 1);
-  if AIndex <= TJArray(self).Count - 1 then
-    TJArray(self).Remove(AIndex).Free;
-  ArrInsert(AIndex, AValue);
-end;
-
-procedure TJValueHelper.SetValue(const APath: string; const AValue: TJValue);
-var
-  LParser: TJSONPathParser;
-  preName: string;
-  jv: TJValue;
-begin
-  if APath.IsEmpty then
-    raise Exception.Create('TJValueHelper.SetValue: path cannot be empty');
-
-  jv := self;
-  LParser := TJSONPathParser.Create(APath);
-  LParser.NextToken;
-  while true do
-  begin
-    preName := LParser.TokenName;
-    LParser.NextToken;
-    case LParser.Token of
-      TJSONPathParser.TToken.Name:
-        jv := jv.GetOrCreate<TJObject>(preName);
-      TJSONPathParser.TToken.ArrayIndex:
-        jv := jv.GetOrCreate<TJArray>(preName);
-      TJSONPathParser.TToken.Eof:
-        begin
-          if jv is TJObject then
-            jv.ObjSetItem(preName, AValue)
-          else
-            jv.ArrSetItem(preName.ToInteger, AValue);
-          break;
-        end;
-    else
-      raise Exception.Create('TJValueHelper.SetValue, LParser.Token Error!');
-    end;
-  end;
-end;
-
-procedure TJValueHelper.TrySetValue(const APath: string; const AValue: TJValue);
-begin
-  try
-    SetValue(APath, AValue);
-  except
-    on E: Exception do
-    begin
-      AValue.Free;
-      raise Exception.Create(E.Message);
-    end;
-  end;
-
-end;
-
-function TJValueHelper.ToType<T>(ADefault: T): T;
-begin
-  if self = nil then
-    Exit(ADefault);
-  try
-    Result := AsType<T>;
-  except
-    Result := ADefault;
-  end;
-end;
-
-function TJValueHelper.GetOrCreate<T>(AName: string): T;
-begin
-  if self is TJObject then
-  begin
-    Result := T(TJObject(self).GetValue(AName));
-    if not(Result is T) then
-    begin
-      Result := T.Create;
-      ObjSetItem(AName, Result);
-    end;
-  end
-  else if self is TJArray then
-  begin
-    ArrFill<TJNull>(AName.ToInteger);
-    Result := T(TJArray(self).Items[AName.ToInteger]);
-    if not(Result is T) then
-    begin
-      Result := T.Create;
-      ArrSetItem(AName.ToInteger, Result);
-    end;
-  end
-  else
-  begin
-    raise Exception.Create('GetOrCreate<T> Error, self must be TJO or TJA');
-  end;
-end;
-
-{ TJValueHelper }
-{ ============================================================================ }
-{ TRPath }
-
-class operator TRPath.Implicit(const Value: string): TRPath;
-begin
-  Result.FData := Value;
-end;
-
-class operator TRPath.Implicit(Value: Integer): TRPath;
-begin
-  Result.FData := '[' + Value.ToString + ']';
-end;
-
-class operator TRPath.Implicit(const [ref] Value: TRPath): string;
-begin
-  Result := Value.FData;
-end;
-
-{ TRPath }
-{ ============================================================================ }
-{ TRJSONEnumerator }
-
-constructor TRJSONEnumerator.Create(const [ref] AData: TRJSON);
-begin
-  inherited Create;
-  FPData := @AData;
-  FIndex := -1;
-end;
-
-function TRJSONEnumerator.GetCurrent: TRJSON;
-var
-  jvTmp: TJValue;
-begin
-  Result.Reset;
-  Result.FIRoot := FPData^.FIRoot;
-  jvTmp := FPData^.GetJValue;
-  if jvTmp is TJObject then
-  begin
-    if FPData^.FPath = '' then
-      Result.FPath := TJObject(jvTmp).Pairs[FIndex].JsonString.Value
-    else
-      Result.FPath := FPData^.FPath + '.' + TJObject(jvTmp).Pairs[FIndex].JsonString.Value;
-  end
-  else if jvTmp is TJArray then
-  begin
-    Result.FPath := FPData^.FPath + '[' + FIndex.ToString + ']';
-  end;
-end;
-
-function TRJSONEnumerator.MoveNext: Boolean;
-begin
-  Inc(FIndex);
-  Exit(FIndex < FPData^.Count)
-end;
-
-{ TRJSONEnumerator }
-{ ============================================================================ }
-{ TRJSON }
-
-function TRJSON.GetEnumerator(): TRJSONEnumerator;
-begin
-  Result := TRJSONEnumerator.Create(self);
-end;
-
-class operator TRJSON.Initialize(out Dest: TRJSON);
-begin
-  Dest.FIRoot := TRJSONRoot.Create;
-  Dest.FPath := '';
-end;
-
-class operator TRJSON.Finalize(var Dest: TRJSON);
-begin
-  Dest.FIRoot := nil;
-end;
-
-function TRJSON.GetRootRefCount: Integer;
-begin
-  Result := (FIRoot as TRJSONRoot).RefCount;
-end;
-
-function TRJSON.ForceRootJValue(const APath: string): TJValue;
-begin
-  if APath.StartsWith('[') then
-    Result := FIRoot.ForceData(TJArray)
-  else
-    Result := FIRoot.ForceData(TJObject);
-end;
-
-function TRJSON.LinkPath(const ALeft, ARight: string): string;
-begin
-  if ALeft.IsEmpty then
-    Result := ARight
-  else if ARight.IsEmpty then
-    Result := ALeft
-  else if ARight.StartsWith('[') then
-    Result := ALeft + ARight
-  else
-    Result := ALeft + '.' + ARight;
-end;
-
-function TRJSON.GetJValue: TJValue;
-begin
-  Result := FIRoot.Data.FindValue(FPath);
-end;
-
-function TRJSON.CloneJValue: TJValue;
-begin
-  Result := GetJValue;
-  if Result <> nil then
-    Result := Result.Clone as TJValue
-  else
-    Result := TJNull.Create;
-end;
-
-class operator TRJSON.Assign(var Dest: TRJSON; const [ref] Src: TRJSON);
-begin
-  if Dest.FPath.IsEmpty then
-  begin
-    Dest.FIRoot := Src.FIRoot;
-    Dest.FPath := Src.FPath;
-  end
-  else
-  begin
-    Dest.SetValue(Src);
-  end;
-end;
-
-class operator TRJSON.Implicit(const Value: string): TRJSON;
-begin
-  Result.FIRoot.Data := TJString.Create(Value);
-end;
-
-class operator TRJSON.Implicit(const [ref] Value: TRJSON): string;
-begin
-  Result := Value.ToStr('');
-end;
-
-class operator TRJSON.Implicit(Value: Integer): TRJSON;
-begin
-  Result.FIRoot.Data := TJNumber.Create(Value);
-end;
-
-class operator TRJSON.Implicit(const [ref] Value: TRJSON): Integer;
-begin
-  Result := Value.ToInt(0);
-end;
-
-class operator TRJSON.Implicit(Value: Int64): TRJSON;
-begin
-  Result.FIRoot.Data := TJNumber.Create(Value);
-end;
-
-class operator TRJSON.Implicit(const [ref] Value: TRJSON): Int64;
-begin
-  Result := Value.ToInt64(0);
-end;
-
-class operator TRJSON.Implicit(Value: Extended): TRJSON;
-begin
-  Result.FIRoot.Data := TJNumber.Create(Value);
-end;
-
-class operator TRJSON.Implicit(const [ref] Value: TRJSON): Extended;
-begin
-  Result := Value.ToFloat(0.0);
-end;
-
-class operator TRJSON.Implicit(Value: Boolean): TRJSON;
-begin
-  Result.FIRoot.Data := TJBool.Create(Value);
-end;
-
-class operator TRJSON.Implicit(const [ref] Value: TRJSON): Boolean;
-begin
-  Result := Value.ToBool(False);
-end;
-
-class operator TRJSON.Implicit(const Value: TJValue): TRJSON;
-begin
-  Result.FIRoot.Data := Value;
-end;
-
-class operator TRJSON.Implicit(const [ref] Value: TRJSON): TJValue;
-begin
-  Result := Value.GetJValue;
-end;
-
-function TRJSON.ToStr(const ADefault: string): string;
-begin
-  Result := FIRoot.Data.FindValue(FPath).ToType<string>(ADefault);
-end;
-
-function TRJSON.ToInt(ADefault: Integer = 0): Integer;
-begin
-  Result := FIRoot.Data.FindValue(FPath).ToType<Integer>(ADefault);
-end;
-
-function TRJSON.ToInt64(ADefault: Int64 = 0): Int64;
-begin
-  Result := FIRoot.Data.FindValue(FPath).ToType<Int64>(ADefault);
-end;
-
-function TRJSON.ToFloat(ADefault: Extended = 0.0): Extended;
-begin
-  Result := FIRoot.Data.FindValue(FPath).ToType<Extended>(ADefault);
-end;
-
-function TRJSON.ToBool(ADefault: Boolean = False): Boolean;
-begin
-  Result := FIRoot.Data.FindValue(FPath).ToType<Boolean>(ADefault);
-end;
-
-function TRJSON.GetItems(const APath: TRPath): TRJSON;
-begin
-  Result.FIRoot := FIRoot;
-  Result.FPath := LinkPath(FPath, APath);
-end;
-
-function TRJSON.GetPairs(AIndex: Integer): TRJSON;
-var
-  jvTmp: TJValue;
-begin
-  jvTmp := GetJValue;
-  if (jvTmp is TJObject) then
-    Result := GetItems(TJObject(jvTmp).Pairs[AIndex].JsonString.Value);
-end;
-
-procedure TRJSON.SetValue(const [ref] AValue: TRJSON);
-var
-  LValue: TJValue;
-begin
-{$IFDEF DEBUG}
-  if FPath.IsEmpty then
-    raise Exception.Create(' TRJSON.SetValue: Path is empty');
-{$ENDIF}
-  LValue := AValue.CloneJValue;
-  try
-    ForceRootJValue(FPath).SetValue(FPath, LValue);
-  except
-    on E: Exception do
-    begin
-      LValue.Free;
-      raise Exception.Create(E.Message);
-    end;
-  end;
-end;
-
-procedure TRJSON.SetItems(const APath: TRPath; const [ref] AValue: TRJSON);
-var
-  tmp: TRJSON;
-begin
-  tmp.FIRoot := FIRoot;
-  tmp.FPath := LinkPath(FPath, APath);
-  tmp.SetValue(AValue)
-end;
-
-function TRJSON.GetS(const APath: TRPath): string;
-var
-  LPath: string;
-begin
-  LPath := LinkPath(FPath, APath);
-  Result := ForceRootJValue(LPath).FindValue(LPath).ToType<string>('');
-end;
-
-procedure TRJSON.SetS(const APath: TRPath; AValue: string);
-var
-  LPath: string;
-begin
-  LPath := LinkPath(FPath, APath);
-  ForceRootJValue(LPath).TrySetValue(LPath, TJString.Create(AValue));
-end;
-
-function TRJSON.GetI(const APath: TRPath): Integer;
-var
-  LPath: string;
-begin
-  LPath := LinkPath(FPath, APath);
-  Result := ForceRootJValue(LPath).FindValue(LPath).ToType<Integer>(0);
-end;
-
-procedure TRJSON.SetI(const APath: TRPath; AValue: Integer);
-var
-  LPath: string;
-begin
-  LPath := LinkPath(FPath, APath);
-  ForceRootJValue(LPath).TrySetValue(LPath, TJNumber.Create(AValue));
-end;
-
-function TRJSON.GetI64(const APath: TRPath): Int64;
-var
-  LPath: string;
-begin
-  LPath := LinkPath(FPath, APath);
-  Result := ForceRootJValue(LPath).FindValue(LPath).ToType<Int64>(0);
-end;
-
-procedure TRJSON.SetI64(const APath: TRPath; AValue: Int64);
-var
-  LPath: string;
-begin
-  LPath := LinkPath(FPath, APath);
-  ForceRootJValue(LPath).TrySetValue(LPath, TJNumber.Create(AValue));
-end;
-
-function TRJSON.GetF(const APath: TRPath): Extended;
-var
-  LPath: string;
-begin
-  LPath := LinkPath(FPath, APath);
-  Result := ForceRootJValue(LPath).FindValue(LPath).ToType<Extended>(0.0);
-end;
-
-procedure TRJSON.SetF(const APath: TRPath; AValue: Extended);
-var
-  LPath: string;
-begin
-  LPath := LinkPath(FPath, APath);
-  ForceRootJValue(LPath).TrySetValue(LPath, TJNumber.Create(AValue));
-end;
-
-function TRJSON.GetB(const APath: TRPath): Boolean;
-var
-  LPath: string;
-begin
-  LPath := LinkPath(FPath, APath);
-  Result := ForceRootJValue(LPath).FindValue(LPath).ToType<Boolean>(False);
-end;
-
-procedure TRJSON.SetB(const APath: TRPath; AValue: Boolean);
-var
-  LPath: string;
-begin
-  LPath := LinkPath(FPath, APath);
-  ForceRootJValue(LPath).TrySetValue(LPath, TJBool.Create(AValue));
-end;
-
-function TRJSON.GetCount: Integer;
-var
-  jvTemp: TJValue;
-begin
-  jvTemp := GetJValue;
-  if jvTemp is TJArray then
-    Result := TJArray(jvTemp).Count
-  else if jvTemp is TJObject then
-    Result := TJObject(jvTemp).Count
-  else
-    Result := 0;
-end;
-
-function TRJSON.GetLastPath: string;
-begin
-  Result := Key;
-  if Result.IsEmpty then
-  begin
-    Result := '[' + Index.ToString + ']';
-    if Result = '[-1]' then
-      Result := '';
-  end;
-end;
-
-function TRJSON.GetIndex: Integer;
-var
-  strTmp: string;
-begin
-  Result := -1;
-  strTmp := FPath.Substring(FPath.LastIndexOf('[') + 1);
-  if strTmp.EndsWith(']') then
-    Result := StrToIntDef(strTmp.TrimRight([']']), -1);
-end;
-
-function TRJSON.GetKey: string;
-begin
-  Result := FPath.Substring(FPath.LastIndexOf('.') + 1);
-  if Result.EndsWith(']') then
-    Result := '';
-end;
-
-function TRJSON.GetRoot: TRJSON;
-begin
-  Result.FIRoot := FIRoot;
-  // Result.FPath := '';
-end;
-
-function TRJSON.IsRoot: Boolean;
-begin
-  Result := FPath.IsEmpty;
-end;
-
-function TRJSON.RootIsJObject: Boolean;
-begin
-  Result := FIRoot.Data is TJObject;
-end;
-
-function TRJSON.RootIsJArray: Boolean;
-begin
-  Result := FIRoot.Data is TJArray;
-end;
-
-function TRJSON.IsJObject: Boolean;
-begin
-  Result := GetJValue is TJObject;
-end;
-
-function TRJSON.IsJArray: Boolean;
-begin
-  Result := GetJValue is TJArray;
-end;
-
-function TRJSON.IsJString: Boolean;
-begin
-  Result := GetJValue is TJString;
-end;
-
-function TRJSON.IsJNumber: Boolean;
-begin
-  Result := GetJValue is TJNumber;
-end;
-
-function TRJSON.IsJBool: Boolean;
-begin
-  Result := GetJValue is TJBool;
-end;
-
-function TRJSON.IsJNull: Boolean;
-begin
-  Result := GetJValue is TJNull;
-end;
-
-function TRJSON.IsNil: Boolean;
-begin
-  Result := GetJValue = nil;
-end;
-
-procedure TRJSON.Reset;
-begin
-  FIRoot := TRJSONRoot.Create;
-  FPath := '';
-end;
-
-function TRJSON.ToJSON(AEncodeBelow32: Boolean = true; AEncodeAbove127: Boolean = true): string;
-var
-  LValue: TJValue;
-  Options: TJSONAncestor.TJSONOutputOptions;
-begin
-  Result := '';
-  LValue := GetJValue;
-  if LValue <> nil then
-  begin
-    Options := [];
-    if AEncodeBelow32 then
-      Include(Options, TJSONAncestor.TJSONOutputOption.EncodeBelow32);
-    if AEncodeAbove127 then
-      Include(Options, TJSONAncestor.TJSONOutputOption.EncodeAbove127);
-    Result := LValue.ToJSON(Options);
-  end;
-end;
-
-function TRJSON.ToString: string;
-begin
-  //Result := ToJSON(False, False);
-  Result := GetJValue.ToString;
-end;
-
-function JSONToUniCode(const AStr: string; AEncodeBelow32: Boolean = true; AEncodeAbove127: Boolean = true): string;
-var
-  ch: char;
-  I: Integer;
-  UnicodeValue: Integer;
-  Buff: array [0 .. 5] of char;
-begin
-  for I := 1 to AStr.Length do
-  begin
-    ch := AStr[I];
-    case ch of
-      #0 .. #7, #$b, #$e .. #31, #$0080 .. High(char):
-        begin
-          UnicodeValue := Ord(ch);
-          if AEncodeBelow32 and (UnicodeValue < 32) or AEncodeAbove127 and (UnicodeValue > 127) then
-          begin
-            Buff[0] := '\';
-            Buff[1] := 'u';
-            Buff[2] := char(DecimalToHex((UnicodeValue and 61440) shr 12));
-            Buff[3] := char(DecimalToHex((UnicodeValue and 3840) shr 8));
-            Buff[4] := char(DecimalToHex((UnicodeValue and 240) shr 4));
-            Buff[5] := char(DecimalToHex((UnicodeValue and 15)));
-            Result := Result + Buff;
-          end
-          else
-          begin
-            Result := Result + ch;
-          end;
-        end
-    else
-      begin
-        Result := Result + ch;
-      end;
-    end;
-  end;
-end;
-
-function TRJSON.Format(AIndentation: Integer; AEncodeBelow32: Boolean; AEncodeAbove127: Boolean): string;
-var
-  LValue: TJValue;
-begin
-  if AIndentation >= 0 then
-  begin
-    Result := '';
-    LValue := GetJValue;
-    if LValue <> nil then
-    begin
-      Result := LValue.Format(AIndentation);
-      if AEncodeBelow32 or AEncodeAbove127 then
-      begin
-        Result := JSONToUniCode(Result, AEncodeBelow32, AEncodeAbove127);
-      end;
-    end;
-  end
-  else
-  begin
-    Result := ToJSON(AEncodeBelow32, AEncodeAbove127);
-  end;
-end;
-
-function TRJSON.ParseJValue(const AData: string; AUseBool: Boolean; ARaiseExc: Boolean): Boolean;
-begin
-  Reset;
-  FIRoot.Data := TJValue.ParseJSONValue(AData, AUseBool, ARaiseExc);
-  Result := FIRoot.Data <> nil;
-end;
-
-function TRJSON.LoadFromFile(const AFileName: string; AUseBool: Boolean; ARaiseExc: Boolean): Boolean;
-begin
-  Result := False;
-  Reset;
-  try
-    FIRoot.Data := TJValue.ParseJSONValue(TFile.ReadAllText(AFileName, TEncoding.UTF8), AUseBool, ARaiseExc);
-    Result := FIRoot.Data <> nil;
-  except
-    on E: Exception do
-    begin
-      if ARaiseExc then
-        raise Exception.Create(E.Message);
-    end;
-  end;
-end;
-
-procedure TRJSON.SaveToFile(const AFileName: string; AIndentation: Integer; AEncodeBelow32: Boolean; AEncodeAbove127: Boolean; AWriteBOM: Boolean);
-var
-  strs: TStrings;
-begin
-  strs := TStringList.Create;
-  try
-    strs.WriteBOM := AWriteBOM;
-    strs.Text := Format(AIndentation, AEncodeBelow32, AEncodeAbove127);
-    strs.SaveToFile(AFileName, TEncoding.UTF8);
-  finally
-    strs.Free;
-  end;
-end;
-
-{ TRJSON }
-{ ============================================================================ }
-
-end.
+{
+  TRJSON - JSON Simple Read and Write
+  - v0.9.14
+  - 2025-05-10 by gale
+  - https://github.com/higale/RJSON
+}
+unit rjson;
+
+interface
+
+uses
+  System.Classes, System.TypInfo, System.SysUtils, System.Json, FMX.Types,
+  System.IOUtils, System.Generics.Collections;
+
+type
+  TJObject = TJSONObject;
+  TJArray = TJSONArray;
+  TJPair = TJSONPair;
+  TJValue = TJSONValue;
+  TJString = TJSONString;
+  TJNumber = TJSONNumber;
+  TJBool = TJSONBool;
+  TJTrue = TJSONTrue;
+  TJFalse = TJSONFalse;
+  TJNull = TJSONNull;
+  TJVType = type of TJValue;
+
+  IRJRoot = interface
+    ['{486F1FA6-2CDD-4124-98C5-CE7C398B7143}']
+    function GetData: TJValue;
+    procedure SetData(const AValue: TJValue);
+    function ForceData(AType: TJVType): TJValue;
+    property Data: TJValue read GetData write SetData;
+  end;
+
+  TRJSONRoot = class(TInterfacedObject, IRJRoot)
+  private
+    FData: TJValue;
+    function GetData: TJValue;
+    procedure SetData(const AValue: TJValue);
+    function ForceData(AType: TJVType): TJValue;
+  public
+    constructor Create;
+    destructor Destroy; override;
+  end;
+
+  TRPath = record
+  private
+    FData: string;
+  public
+    class operator Implicit(const Value: string): TRPath;
+    class operator Implicit(Value: Integer): TRPath;
+    class operator Implicit(const [ref] Value: TRPath): string;
+  end;
+
+  TRJSONEnumerator = class;
+
+  TRJSON = record
+  private
+    FIRoot: IRJRoot;
+    FPath: string;
+    function GetRootRefCount: Integer;
+    function ForceRootJValue(const APath: string): TJValue;
+    function LinkPath(const ALeft, ARight: string): string;
+    function GetJValue: TJValue;
+    function GetItems(const APath: TRPath): TRJSON;
+    function GetPairs(AIndex: Integer): TRJSON;
+    procedure SetValue(const [ref] AValue: TRJSON);
+    procedure SetItems(const APath: TRPath; const [ref] AValue: TRJSON);
+    function GetS(const APath: TRPath): string; overload;
+    procedure SetS(const APath: TRPath; AValue: string); overload;
+    function GetI(const APath: TRPath): Integer; overload;
+    procedure SetI(const APath: TRPath; AValue: Integer); overload;
+    function GetI64(const APath: TRPath): Int64; overload;
+    procedure SetI64(const APath: TRPath; AValue: Int64); overload;
+    function GetF(const APath: TRPath): Extended; overload;
+    procedure SetF(const APath: TRPath; AValue: Extended); overload;
+    function GetB(const APath: TRPath): Boolean; overload;
+    procedure SetB(const APath: TRPath; AValue: Boolean); overload;
+    function GetCount: Integer;
+    function GetLastPath: string;
+    function GetIndex: Integer;
+    function GetKey: string;
+    function GetRoot: TRJSON;
+    function GetParent: TRJSON;
+  public
+    function GetEnumerator(): TRJSONEnumerator;
+    class operator Initialize(out Dest: TRJSON);
+    class operator Finalize(var Dest: TRJSON);
+    class operator Assign(var Dest: TRJSON; const [ref] Src: TRJSON);
+    class operator Implicit(const Value: string): TRJSON;
+    class operator Implicit(const [ref] Value: TRJSON): string;
+    class operator Implicit(Value: Integer): TRJSON;
+    class operator Implicit(const [ref] Value: TRJSON): Integer;
+    class operator Implicit(Value: Int64): TRJSON;
+    class operator Implicit(const [ref] Value: TRJSON): Int64;
+    class operator Implicit(Value: Extended): TRJSON;
+    class operator Implicit(const [ref] Value: TRJSON): Extended;
+    class operator Implicit(Value: Boolean): TRJSON;
+    class operator Implicit(const [ref] Value: TRJSON): Boolean;
+    class operator Implicit(const Value: TJValue): TRJSON;
+    class operator Implicit(const [ref] Value: TRJSON): TJValue;
+    function ToStr(const ADefault: string = ''): string;
+    function ToInt(ADefault: Integer = 0): Integer;
+    function ToInt64(ADefault: Int64 = 0): Int64;
+    function ToFloat(ADefault: Extended = 0.0): Extended;
+    function ToBool(ADefault: Boolean = False): Boolean;
+
+    property Items[const APath: TRPath]: TRJSON read GetItems write SetItems; default;
+    property S[const APath: TRPath]: string read GetS write SetS;
+    property I[const APath: TRPath]: Integer read GetI write SetI;
+    property I64[const APath: TRPath]: Int64 read GetI64 write SetI64;
+    property F[const APath: TRPath]: Extended read GetF write SetF;
+    property B[const APath: TRPath]: Boolean read GetB write SetB;
+    property Pairs[AIndex: Integer]: TRJSON read GetPairs;
+    property Count: Integer read GetCount;
+    property LastPath: string read GetLastPath;
+    property Index: Integer read GetIndex;
+    property Key: string read GetKey;
+    property RootRefCount: Integer read GetRootRefCount;
+    property Root: TRJSON read GetRoot;
+    property Parent: TRJSON read GetParent;
+    property Path: string read FPath;
+    property JValue: TJValue read GetJValue;
+
+    function CloneJValue: TJValue;
+    function IsRoot: Boolean; inline;
+    function RootIsObject: Boolean; inline;
+    function RootIsArray: Boolean; inline;
+    function IsObject: Boolean;
+    function IsArray: Boolean;
+    function IsString: Boolean;
+    function IsNumber: Boolean;
+    function IsInt: Boolean;
+    function IsInteger: Boolean;
+    function IsFloat: Boolean;
+    function IsBool: Boolean;
+    function IsNull: Boolean;
+    function IsNil: Boolean;
+    function IsEmpty: Boolean;
+    function ItemByValue(const [ref] AValue: TRJSON; AIgnoreCase: Boolean = True): TRJSON;
+    function FirstItem: TRJSON;
+    function LastItem: TRJSON;
+    procedure MoveTo(AIndex: Integer);
+    procedure Rename(AName: string);
+    procedure MoveUp;
+    procedure MoveDown;
+    procedure MoveToFirst;
+    procedure MoveToLast;
+
+    procedure Add(const AValue: TRJSON);
+    procedure Delete(AIndex: Integer); overload;
+    procedure Delete; overload;
+
+    procedure Reset;
+    function ToJSON(AEncodeBelow32: Boolean = True; AEncodeAbove127: Boolean = True): string;
+    function Format(AIndentation: Integer = 4; AEncodeBelow32: Boolean = False; AEncodeAbove127: Boolean = False): string;
+    procedure ParseJValue(const AData: string; AUseBool: Boolean = False; ARaiseExc: Boolean = False);
+    procedure LoadFromFile(const AFileName: string; AUseBool: Boolean = False; ARaiseExc: Boolean = False);
+    procedure SaveToFile(const AFileName: string; AIndentation: Integer = 4; AEncodeBelow32: Boolean = True; AEncodeAbove127: Boolean = False; AWriteBOM: Boolean = True);
+    procedure LoadFromObject(const AObject: TObject; ARaiseExc: Boolean = False);
+    procedure SetObjectProp(AObject: TObject; ARaiseExc: Boolean = False);
+  end;
+
+  { Iterators }
+  TRJSONEnumerator = class
+  private
+    FPData: ^TRJSON;
+    FIndex: Integer;
+    function GetCurrent: TRJSON;
+  public
+    constructor Create(const [ref] AData: TRJSON);
+    function MoveNext: Boolean;
+    property Current: TRJSON read GetCurrent;
+  end;
+
+implementation
+
+{ ============================================================================ }
+{ TRJSONRoot }
+
+constructor TRJSONRoot.Create;
+begin
+  inherited;
+  FData := nil;
+end;
+
+destructor TRJSONRoot.Destroy;
+begin
+  FData.Free;
+  inherited;
+end;
+
+function TRJSONRoot.GetData: TJValue;
+begin
+  Result := FData;
+end;
+
+procedure TRJSONRoot.SetData(const AValue: TJValue);
+begin
+  FData := AValue;
+end;
+
+function TRJSONRoot.ForceData(AType: TJVType): TJValue;
+begin
+  if not(FData is AType) then
+  begin
+    FData.Free;
+    FData := AType.Create;
+  end;
+  Result := FData;
+end;
+
+{ TRJSONRoot }
+{ ============================================================================ }
+{ TJValueHelper TJObjectHelper TJArrayHelper}
+
+type
+  TJValueHelper = class helper for TJValue
+  private
+    function ToType<T>(ADefault: T): T;
+    function GetOrCreate<T: TJValue>(AName: string): T;
+    procedure SetValue(const APath: string; const AValue: TJValue);
+    procedure TrySetValue(const APath: string; const AValue: TJValue);
+  end;
+
+  TJObjectHelper = class helper for TJObject
+  private
+    procedure _SetItem(const AName: string; const AValue: TJValue); overload;
+   // procedure _Insert(const AIndex: Integer; const AKey: string; const AValue: TJValue); overload;
+    procedure _Insert(const AIndex: Integer; const AValue: TJPair); overload;
+  end;
+
+  TJArrayHelper = class helper for TJArray
+  private
+    procedure _Fill<T: TJValue>(ACount: Integer);
+    procedure _Insert(const AIndex: Integer; const AValue: TJValue);
+    procedure _SetItem(AIndex: Integer; const AValue: TJValue); overload;
+  end;
+
+  TJSONPairHelper = class helper for TJSONPair
+  private
+    procedure Rename(ANewName: string);
+  end;
+
+procedure TJSONPairHelper.Rename(ANewName: string);
+begin
+  SetJsonString(TJSONString.Create(ANewName));
+end;
+
+procedure TJArrayHelper._Fill<T>(ACount: Integer);
+begin
+  for var j := Count to ACount do
+    AddElement(T.Create);
+end;
+
+procedure TJArrayHelper._Insert(const AIndex: Integer; const AValue: TJValue);
+begin
+  AddElement(AValue);
+  for var I := AIndex to Count - 2 do
+    AddElement(Remove(AIndex));
+end;
+
+procedure TJArrayHelper._SetItem(AIndex: Integer; const AValue: TJValue);
+begin
+  _Fill<TJNull>(AIndex - 1);
+  if AIndex <= Count - 1 then
+    Remove(AIndex).Free;
+  _Insert(AIndex, AValue);
+end;
+
+procedure TJValueHelper.SetValue(const APath: string; const AValue: TJValue);
+var
+  LParser: TJSONPathParser;
+  preName: string;
+  jv: TJValue;
+begin
+  if APath.IsEmpty then
+    raise Exception.Create('TJValueHelper.SetValue: path cannot be empty');
+  jv := self;
+  LParser := TJSONPathParser.Create(APath);
+  LParser.NextToken;
+  while True do
+  begin
+    preName := LParser.TokenName;
+    LParser.NextToken;
+    case LParser.Token of
+      TJSONPathParser.TToken.Name:
+        jv := jv.GetOrCreate<TJObject>(preName);
+      TJSONPathParser.TToken.ArrayIndex:
+        jv := jv.GetOrCreate<TJArray>(preName);
+      TJSONPathParser.TToken.Eof:
+        begin
+          if jv is TJObject then
+            TJObject(jv)._SetItem(preName, AValue)
+          else
+            TJArray(jv)._SetItem(preName.ToInteger, AValue);
+          break;
+        end;
+    else
+      raise Exception.Create('TJValueHelper.SetValue, LParser.Token Error!');
+    end;
+  end;
+end;
+
+procedure TJValueHelper.TrySetValue(const APath: string; const AValue: TJValue);
+begin
+  try
+    SetValue(APath, AValue);
+  except
+    on E: Exception do
+    begin
+      AValue.Free;
+      raise Exception.Create(E.Message);
+    end;
+  end;
+end;
+
+function TJValueHelper.ToType<T>(ADefault: T): T;
+begin
+  if self = nil then
+    Exit(ADefault);
+  try
+    Result := AsType<T>;
+  except
+    Result := ADefault;
+  end;
+end;
+
+function TJValueHelper.GetOrCreate<T>(AName: string): T;
+begin
+  if self is TJObject then
+  begin
+    Result := T(TJObject(self).GetValue(AName));
+    if not(Result is T) then
+    begin
+      Result := T.Create;
+      TJObject(self)._SetItem(AName, Result);
+    end;
+  end
+  else if self is TJArray then
+  begin
+    TJArray(self)._Fill<TJNull>(AName.ToInteger);
+    Result := T(TJArray(self).Items[AName.ToInteger]);
+    if not(Result is T) then
+    begin
+      Result := T.Create;
+      TJArray(self)._SetItem(AName.ToInteger, Result);
+    end;
+  end
+  else
+  begin
+    raise Exception.Create('GetOrCreate<T> Error, self must be TJO or TJA');
+  end;
+end;
+
+procedure TJObjectHelper._SetItem(const AName: string; const AValue: TJValue);
+var
+  pairTmp: TJSONPair;
+begin
+  pairTmp := Get(AName);
+  if pairTmp = nil then
+    AddPair(AName, AValue)
+  else
+    pairTmp.JSONValue := AValue;
+end;
+
+{procedure TJObjectHelper._Insert(const AIndex: Integer; const AKey: string; const AValue: TJValue);
+begin
+  with self do
+  begin
+    FMembers.Insert(AIndex, TJSONPair.Create(AKey, AValue));
+  end;
+end;}
+
+procedure TJObjectHelper._Insert(const AIndex: Integer; const AValue: TJPair);
+begin
+  with self do
+  begin
+    FMembers.Insert(AIndex, AValue);
+  end;
+end;
+
+{ TJValueHelper TJObjectHelper TJArrayHelper}
+{ ============================================================================ }
+{ TRPath }
+
+class operator TRPath.Implicit(const Value: string): TRPath;
+begin
+  Result.FData := Value;
+end;
+
+class operator TRPath.Implicit(Value: Integer): TRPath;
+begin
+  Result.FData := '[' + Value.ToString + ']';
+end;
+
+class operator TRPath.Implicit(const [ref] Value: TRPath): string;
+begin
+  Result := Value.FData;
+end;
+
+{ TRPath }
+{ ============================================================================ }
+{ TRJSONEnumerator }
+
+constructor TRJSONEnumerator.Create(const [ref] AData: TRJSON);
+begin
+  inherited Create;
+  FPData := @AData;
+  FIndex := -1;
+end;
+
+function TRJSONEnumerator.GetCurrent: TRJSON;
+var
+  jvTmp: TJValue;
+begin
+  Result.Reset;
+  Result.FIRoot := FPData^.FIRoot;
+  jvTmp := FPData^.GetJValue;
+  if jvTmp is TJObject then
+  begin
+    if FPData^.FPath = '' then
+      Result.FPath := TJObject(jvTmp).Pairs[FIndex].JsonString.Value
+    else
+      Result.FPath := FPData^.FPath + '.' + TJObject(jvTmp).Pairs[FIndex].JsonString.Value;
+  end
+  else if jvTmp is TJArray then
+  begin
+    Result.FPath := FPData^.FPath + '[' + FIndex.ToString + ']';
+  end;
+end;
+
+function TRJSONEnumerator.MoveNext: Boolean;
+begin
+  Inc(FIndex);
+  Exit(FIndex < FPData^.Count)
+end;
+
+{ TRJSONEnumerator }
+{ ============================================================================ }
+{ TRJSON }
+
+function TRJSON.GetEnumerator(): TRJSONEnumerator;
+begin
+  Result := TRJSONEnumerator.Create(self);
+end;
+
+class operator TRJSON.Initialize(out Dest: TRJSON);
+begin
+  Dest.FIRoot := TRJSONRoot.Create;
+  Dest.FPath := '';
+end;
+
+class operator TRJSON.Finalize(var Dest: TRJSON);
+begin
+  Dest.FIRoot := nil;
+end;
+
+function TRJSON.GetRootRefCount: Integer;
+begin
+  Result := (FIRoot as TRJSONRoot).RefCount;
+end;
+
+function TRJSON.ForceRootJValue(const APath: string): TJValue;
+begin
+  if APath.StartsWith('[') then
+    Result := FIRoot.ForceData(TJArray)
+  else
+    Result := FIRoot.ForceData(TJObject);
+end;
+
+function TRJSON.LinkPath(const ALeft, ARight: string): string;
+begin
+  if ALeft.IsEmpty then
+    Result := ARight
+  else if ARight.IsEmpty then
+    Result := ALeft
+  else if ARight.StartsWith('[') then
+    Result := ALeft + ARight
+  else
+    Result := ALeft + '.' + ARight;
+end;
+
+function TRJSON.GetJValue: TJValue;
+begin
+  Result := FIRoot.Data.FindValue(FPath);
+end;
+
+function TRJSON.CloneJValue: TJValue;
+var
+  LValue: TJValue;
+begin
+  LValue := GetJValue;
+  if LValue <> nil then
+    Exit(TJValue(LValue.Clone));
+  Result := nil;
+end;
+
+class operator TRJSON.Assign(var Dest: TRJSON; const [ref] Src: TRJSON);
+begin
+  if Dest.FPath.IsEmpty then
+  begin
+    Dest.FIRoot := Src.FIRoot;
+    Dest.FPath := Src.FPath;
+  end
+  else
+  begin
+    Dest.SetValue(Src);
+  end;
+end;
+
+class operator TRJSON.Implicit(const Value: string): TRJSON;
+begin
+  Result.FIRoot.Data := TJString.Create(Value);
+end;
+
+class operator TRJSON.Implicit(const [ref] Value: TRJSON): string;
+begin
+  Result := Value.ToStr('');
+end;
+
+class operator TRJSON.Implicit(Value: Integer): TRJSON;
+begin
+  Result.FIRoot.Data := TJNumber.Create(Value);
+end;
+
+class operator TRJSON.Implicit(const [ref] Value: TRJSON): Integer;
+begin
+  Result := Value.ToInt(0);
+end;
+
+class operator TRJSON.Implicit(Value: Int64): TRJSON;
+begin
+  Result.FIRoot.Data := TJNumber.Create(Value);
+end;
+
+class operator TRJSON.Implicit(const [ref] Value: TRJSON): Int64;
+begin
+  Result := Value.ToInt64(0);
+end;
+
+class operator TRJSON.Implicit(Value: Extended): TRJSON;
+begin
+  Result.FIRoot.Data := TJNumber.Create(Value);
+end;
+
+class operator TRJSON.Implicit(const [ref] Value: TRJSON): Extended;
+begin
+  Result := Value.ToFloat(0.0);
+end;
+
+class operator TRJSON.Implicit(Value: Boolean): TRJSON;
+begin
+  Result.FIRoot.Data := TJBool.Create(Value);
+end;
+
+class operator TRJSON.Implicit(const [ref] Value: TRJSON): Boolean;
+begin
+  Result := Value.ToBool(False);
+end;
+
+class operator TRJSON.Implicit(const Value: TJValue): TRJSON;
+begin
+  Result.FIRoot.Data := Value;
+end;
+
+class operator TRJSON.Implicit(const [ref] Value: TRJSON): TJValue;
+begin
+  Result := Value.GetJValue;
+end;
+
+function TRJSON.ToStr(const ADefault: string): string;
+begin
+  Result := FIRoot.Data.FindValue(FPath).ToType<string>(ADefault);
+end;
+
+function TRJSON.ToInt(ADefault: Integer = 0): Integer;
+begin
+  Result := FIRoot.Data.FindValue(FPath).ToType<Integer>(ADefault);
+end;
+
+function TRJSON.ToInt64(ADefault: Int64 = 0): Int64;
+begin
+  Result := FIRoot.Data.FindValue(FPath).ToType<Int64>(ADefault);
+end;
+
+function TRJSON.ToFloat(ADefault: Extended = 0.0): Extended;
+begin
+  Result := FIRoot.Data.FindValue(FPath).ToType<Extended>(ADefault);
+end;
+
+function TRJSON.ToBool(ADefault: Boolean = False): Boolean;
+begin
+  Result := FIRoot.Data.FindValue(FPath).ToType<Boolean>(ADefault);
+end;
+
+function TRJSON.GetItems(const APath: TRPath): TRJSON;
+begin
+  Result.FIRoot := FIRoot;
+  Result.FPath := LinkPath(FPath, APath);
+end;
+
+function TRJSON.GetPairs(AIndex: Integer): TRJSON;
+var
+  jvTmp: TJValue;
+begin
+  jvTmp := GetJValue;
+  if (jvTmp is TJObject) then
+    Result := GetItems(TJObject(jvTmp).Pairs[AIndex].JsonString.Value);
+end;
+
+procedure TRJSON.SetValue(const [ref] AValue: TRJSON);
+var
+  LValue: TJValue;
+begin
+  LValue := AValue.CloneJValue;
+  if LValue = nil then
+    LValue := TJNull.Create;
+  try
+    ForceRootJValue(FPath).SetValue(FPath, LValue);
+  except
+    on E: Exception do
+    begin
+      LValue.Free;
+      raise Exception.Create(E.Message);
+    end;
+  end;
+end;
+
+procedure TRJSON.SetItems(const APath: TRPath; const [ref] AValue: TRJSON);
+var
+  tmp: TRJSON;
+begin
+  tmp.FIRoot := FIRoot;
+  tmp.FPath := LinkPath(FPath, APath);
+  tmp.SetValue(AValue)
+end;
+
+function TRJSON.GetS(const APath: TRPath): string;
+var
+  LPath: string;
+begin
+  LPath := LinkPath(FPath, APath);
+  Result := ForceRootJValue(LPath).FindValue(LPath).ToType<string>('');
+end;
+
+procedure TRJSON.SetS(const APath: TRPath; AValue: string);
+var
+  LPath: string;
+begin
+  LPath := LinkPath(FPath, APath);
+  ForceRootJValue(LPath).TrySetValue(LPath, TJString.Create(AValue));
+end;
+
+function TRJSON.GetI(const APath: TRPath): Integer;
+var
+  LPath: string;
+begin
+  LPath := LinkPath(FPath, APath);
+  Result := ForceRootJValue(LPath).FindValue(LPath).ToType<Integer>(0);
+end;
+
+procedure TRJSON.SetI(const APath: TRPath; AValue: Integer);
+var
+  LPath: string;
+begin
+  LPath := LinkPath(FPath, APath);
+  ForceRootJValue(LPath).TrySetValue(LPath, TJNumber.Create(AValue));
+end;
+
+function TRJSON.GetI64(const APath: TRPath): Int64;
+var
+  LPath: string;
+begin
+  LPath := LinkPath(FPath, APath);
+  Result := ForceRootJValue(LPath).FindValue(LPath).ToType<Int64>(0);
+end;
+
+procedure TRJSON.SetI64(const APath: TRPath; AValue: Int64);
+var
+  LPath: string;
+begin
+  LPath := LinkPath(FPath, APath);
+  ForceRootJValue(LPath).TrySetValue(LPath, TJNumber.Create(AValue));
+end;
+
+function TRJSON.GetF(const APath: TRPath): Extended;
+var
+  LPath: string;
+begin
+  LPath := LinkPath(FPath, APath);
+  Result := ForceRootJValue(LPath).FindValue(LPath).ToType<Extended>(0.0);
+end;
+
+procedure TRJSON.SetF(const APath: TRPath; AValue: Extended);
+var
+  LPath: string;
+begin
+  LPath := LinkPath(FPath, APath);
+  ForceRootJValue(LPath).TrySetValue(LPath, TJNumber.Create(AValue));
+end;
+
+function TRJSON.GetB(const APath: TRPath): Boolean;
+var
+  LPath: string;
+begin
+  LPath := LinkPath(FPath, APath);
+  Result := ForceRootJValue(LPath).FindValue(LPath).ToType<Boolean>(False);
+end;
+
+procedure TRJSON.SetB(const APath: TRPath; AValue: Boolean);
+var
+  LPath: string;
+begin
+  LPath := LinkPath(FPath, APath);
+  ForceRootJValue(LPath).TrySetValue(LPath, TJBool.Create(AValue));
+end;
+
+function TRJSON.GetCount: Integer;
+var
+  jvTemp: TJValue;
+begin
+  jvTemp := GetJValue;
+  if jvTemp is TJArray then
+    Result := TJArray(jvTemp).Count
+  else if jvTemp is TJObject then
+    Result := TJObject(jvTemp).Count
+  else
+    Result := 0;
+end;
+
+function TRJSON.GetLastPath: string;
+begin
+  if FPath.IsEmpty then
+    Exit('');
+  Result := Key;
+  if Result.IsEmpty then
+  begin
+    Result := '[' + Index.ToString + ']';
+    if Result = '[-1]' then
+      Result := '';
+  end;
+end;
+
+function TRJSON.GetIndex: Integer;
+var
+  strTmp: string;
+begin
+  Result := -1;
+  strTmp := FPath.Substring(FPath.LastIndexOf('[') + 1);
+  if strTmp.EndsWith(']') then
+    Result := StrToIntDef(strTmp.TrimRight([']']), -1);
+end;
+
+function TRJSON.GetKey: string;
+begin
+  Result := FPath.Substring(FPath.LastIndexOf('.') + 1);
+  if Result.EndsWith(']') then
+    Result := '';
+end;
+
+function TRJSON.GetRoot: TRJSON;
+begin
+  Result.FIRoot := FIRoot;
+end;
+
+function TRJSON.GetParent: TRJSON;
+var
+  iPos: Integer;
+begin
+  if FPath.IsEmpty then
+    Exit;
+  iPos := FPath.LastIndexOfAny(['[', '.']);
+  if iPos < 0 then
+    Exit(Root);
+  Result.FIRoot := FIRoot;
+  Result.FPath := FPath.Substring(0, iPos);
+end;
+
+function TRJSON.IsRoot: Boolean;
+begin
+  Result := FPath.IsEmpty;
+end;
+
+function TRJSON.RootIsObject: Boolean;
+begin
+  Result := FIRoot.Data is TJObject;
+end;
+
+function TRJSON.RootIsArray: Boolean;
+begin
+  Result := FIRoot.Data is TJArray;
+end;
+
+function TRJSON.IsObject: Boolean;
+begin
+  Result := GetJValue is TJObject;
+end;
+
+function TRJSON.IsArray: Boolean;
+begin
+  Result := GetJValue is TJArray;
+end;
+
+function TRJSON.IsString: Boolean;
+begin
+  if JValue <> nil then
+    Exit(JValue.ClassName = 'TJSONString');
+  Result := False;
+end;
+
+function TRJSON.IsNumber: Boolean;
+begin
+  Result := GetJValue is TJNumber;
+end;
+
+function TRJSON.IsInt: Boolean;
+begin
+  if IsNumber then
+    Exit(ToStr.IndexOf('.') < 0);
+  Result := False;
+end;
+
+function TRJSON.IsInteger: Boolean;
+var
+  LI64: Int64;
+begin
+  if IsInt then
+  begin
+    LI64 := ToInt64;
+    Exit((LI64 >= Integer.MinValue) and (LI64 <= Integer.MaxValue));
+  end;
+  Result := False;
+end;
+
+function TRJSON.IsFloat: Boolean;
+begin
+  if IsNumber then
+    Exit(ToStr.IndexOf('.') >= 0);
+  Result := False;
+end;
+
+function TRJSON.IsBool: Boolean;
+begin
+  Result := GetJValue is TJBool;
+end;
+
+function TRJSON.IsNull: Boolean;
+begin
+  Result := GetJValue is TJNull;
+end;
+
+function TRJSON.IsNil: Boolean;
+begin
+  Result := GetJValue = nil;
+end;
+
+function TRJSON.IsEmpty: Boolean;
+begin
+  Result := FPath.IsEmpty and (FIRoot.Data = nil);
+end;
+
+function TRJSON.ItemByValue(const [ref] AValue: TRJSON; AIgnoreCase: Boolean): TRJSON;
+begin
+  for var item in self do
+  begin
+    if AValue.JValue.ClassType = item.JValue.ClassType then
+      if string.Compare(AValue.ToStr, item.ToStr, AIgnoreCase) = 0 then
+        Exit(item);
+  end;
+end;
+
+function TRJSON.FirstItem: TRJSON;
+begin
+  if IsArray then
+    Result := Items[0]
+  else if IsObject then
+    Result := Pairs[0]
+end;
+
+function TRJSON.LastItem: TRJSON;
+begin
+  if IsArray then
+    Result := Items[Count - 1]
+  else if IsObject then
+    Result := Pairs[Count - 1]
+end;
+
+procedure TRJSON.MoveTo(AIndex: Integer);
+var
+  LParent: TJValue;
+  LParentTmp: TRJSON;
+  LValue: TRJSON;
+begin
+  LParent := Parent.JValue;
+  if (AIndex >= Parent.Count) or (AIndex < 0) then
+    raise Exception.Create('Index out of bounds');
+  if LParent is TJArray then
+  begin
+    TJArray(LParent)._Insert(AIndex, TJArray(LParent).Remove(Index));
+  end
+  else if LParent is TJObject then
+  begin
+    TJObject(LParent)._Insert(AIndex, TJObject(LParent).RemovePair(Key));
+  end;
+end;
+
+procedure TRJSON.Rename(AName: string);
+begin
+  if Parent.IsObject then
+    TJObject(Parent.JValue).Get(Key).Rename(AName);
+
+  if Parent.IsRoot then
+    FPath := AName
+  else
+    FPath := FPath.Substring(0, FPath.LastIndexOf('.') + 1) + AName;
+end;
+
+procedure TRJSON.MoveUp;
+begin
+  if Index > 0 then
+    MoveTo(Index - 1);
+end;
+
+procedure TRJSON.MoveDown;
+begin
+  if Index < Parent.Count - 1 then
+    MoveTo(Index + 1);
+end;
+
+procedure TRJSON.MoveToFirst;
+begin
+  MoveTo(0);
+end;
+
+procedure TRJSON.MoveToLast;
+begin
+  MoveTo(Parent.Count - 1);
+end;
+
+procedure TRJSON.Add(const AValue: TRJSON);
+begin
+  if IsArray then
+  begin
+    Items[Count] := AValue.CloneJValue;
+  end
+  else
+  begin
+    Items[0] := AValue.CloneJValue;
+  end;
+end;
+
+procedure TRJSON.Delete(AIndex: Integer);
+begin
+  if IsArray then
+  begin
+    TJArray(GetJValue).Remove(AIndex).Free;
+  end;
+end;
+
+procedure TRJSON.Delete;
+var
+  LParentValue: TJValue;
+begin
+  if IsRoot then
+    Reset;
+  LParentValue := Parent.JValue;
+  if LParentValue is TJObject then
+  begin
+    TJObject(LParentValue).RemovePair(Key).Free;
+  end
+  else if LParentValue is TJArray then
+  begin
+    TJArray(LParentValue).Remove(Index).Free;
+  end;
+end;
+
+procedure TRJSON.Reset;
+begin
+  FIRoot := TRJSONRoot.Create;
+  FPath := '';
+end;
+
+function TRJSON.ToJSON(AEncodeBelow32: Boolean = True; AEncodeAbove127: Boolean = True): string;
+var
+  LValue: TJValue;
+  Options: TJSONAncestor.TJSONOutputOptions;
+begin
+  Result := '';
+  LValue := GetJValue;
+  if LValue <> nil then
+  begin
+    Options := [];
+    if AEncodeBelow32 then
+      Include(Options, TJSONAncestor.TJSONOutputOption.EncodeBelow32);
+    if AEncodeAbove127 then
+      Include(Options, TJSONAncestor.TJSONOutputOption.EncodeAbove127);
+    Result := LValue.ToJSON(Options);
+  end;
+end;
+
+function JSONToUniCode(const AStr: string; AEncodeBelow32: Boolean = True; AEncodeAbove127: Boolean = True): string;
+var
+  ch: char;
+  I: Integer;
+  UnicodeValue: Integer;
+  Buff: array [0 .. 5] of char;
+begin
+  for I := 1 to AStr.Length do
+  begin
+    ch := AStr[I];
+    case ch of
+      #0 .. #7, #$b, #$e .. #31, #$0080 .. High(char):
+        begin
+          UnicodeValue := Ord(ch);
+          if AEncodeBelow32 and (UnicodeValue < 32) or AEncodeAbove127 and (UnicodeValue > 127) then
+          begin
+            Buff[0] := '\';
+            Buff[1] := 'u';
+            Buff[2] := char(DecimalToHex((UnicodeValue and 61440) shr 12));
+            Buff[3] := char(DecimalToHex((UnicodeValue and 3840) shr 8));
+            Buff[4] := char(DecimalToHex((UnicodeValue and 240) shr 4));
+            Buff[5] := char(DecimalToHex((UnicodeValue and 15)));
+            Result := Result + Buff;
+          end
+          else
+          begin
+            Result := Result + ch;
+          end;
+        end
+    else
+      begin
+        Result := Result + ch;
+      end;
+    end;
+  end;
+end;
+
+function TRJSON.Format(AIndentation: Integer; AEncodeBelow32: Boolean; AEncodeAbove127: Boolean): string;
+var
+  LValue: TJValue;
+begin
+  if AIndentation >= 0 then
+  begin
+    Result := '';
+    LValue := GetJValue;
+    if LValue <> nil then
+    begin
+      Result := LValue.Format(AIndentation);
+      if AEncodeBelow32 or AEncodeAbove127 then
+      begin
+        Result := JSONToUniCode(Result, AEncodeBelow32, AEncodeAbove127);
+      end;
+    end;
+  end
+  else
+  begin
+    Result := ToJSON(AEncodeBelow32, AEncodeAbove127);
+  end;
+end;
+
+procedure TRJSON.ParseJValue(const AData: string; AUseBool: Boolean; ARaiseExc: Boolean);
+begin
+  self := TJValue.ParseJSONValue(AData, AUseBool, ARaiseExc);
+end;
+
+procedure TRJSON.LoadFromFile(const AFileName: string; AUseBool: Boolean; ARaiseExc: Boolean);
+begin
+  try
+    ParseJValue(TFile.ReadAllText(AFileName, TEncoding.UTF8), AUseBool, ARaiseExc);
+  except
+    on E: Exception do
+    begin
+      if ARaiseExc then
+        raise Exception.Create(E.Message);
+    end;
+  end;
+end;
+
+procedure TRJSON.SaveToFile(const AFileName: string; AIndentation: Integer; AEncodeBelow32: Boolean; AEncodeAbove127: Boolean; AWriteBOM: Boolean);
+var
+  strs: TStrings;
+begin
+  strs := TStringList.Create;
+  try
+    strs.WriteBOM := AWriteBOM;
+    strs.Text := Format(AIndentation, AEncodeBelow32, AEncodeAbove127);
+    strs.SaveToFile(AFileName, TEncoding.UTF8);
+  finally
+    strs.Free;
+  end;
+end;
+
+procedure TRJSON.LoadFromObject(const AObject: TObject; ARaiseExc: Boolean);
+var
+  PropName: string;
+  PropType: string;
+  PropEnumName: string;
+  propList: PPropList;
+  PropValue: Variant;
+  rjTmp: TRJSON;
+begin
+  GetPropList(AObject.ClassInfo, propList);
+  try
+    for var I := 0 to GetTypeData(AObject.ClassInfo).propCount - 1 do
+    begin
+      try
+        PropName := string(propList[I]^.Name);
+        PropType := string(propList[I]^.PropType^.Name);
+        PropEnumName := GetEnumName(TypeInfo(TTypeKind), Int64(propList[I]^.PropType^.Kind));
+        PropValue := GetPropValue(AObject, PropName, True);
+
+        // if PropType = 'TComponentName' then
+         // Continue;
+        if PropName = 'ActiveControl' then
+          Continue;
+
+        if propList[I]^.PropType^.Kind <> tkMethod then
+        begin
+          // rjTmp[PropName + '_dbg_inf'] := PropType + ' ' + PropEnumName;
+          if (propList[I]^.PropType^.Kind <> tkClass) then
+          begin
+            if PropType = 'Int64' then
+              rjTmp[PropName] := Int64(PropValue)
+            else if PropType = 'Integer' then
+              rjTmp[PropName] := Integer(PropValue)
+            else if PropType = 'Boolean' then
+              rjTmp[PropName] := Boolean(PropValue)
+            else if PropType = 'TAlphaColor' then
+              rjTmp[PropName] := '#' + {$IFDEF CPUX64}Int64{$ELSE}Integer{$ENDIF}(PropValue).ToHexString(8)
+            else
+            begin
+              case propList[I]^.PropType^.Kind of
+                tkInteger:
+                  rjTmp[PropName] := {$IFDEF CPUX64}Int64{$ELSE}Integer{$ENDIF}(PropValue);
+                tkInt64:
+                  rjTmp[PropName] := Int64(PropValue);
+                tkFloat:
+                  rjTmp[PropName] := Extended(PropValue);
+              else // tkEnumeration, tkSet, tkUString
+                rjTmp[PropName] := string(PropValue);
+              end;
+            end;
+          end
+          else if PropValue <> 0 then
+          begin
+            rjTmp[PropName].LoadFromObject(TObject(StrToInt64(PropValue)));
+          end;
+        end;
+      except
+        on E: Exception do
+          if ARaiseExc then
+            raise Exception.Create(E.Message);
+      end;
+    end;
+    self := rjTmp;
+  finally
+    FreeMem(propList);
+  end;
+end;
+
+procedure TRJSON.SetObjectProp(AObject: TObject; ARaiseExc: Boolean);
+var
+  PropName: string;
+  PropInfo: PPropInfo;
+begin
+  for var item in self do
+  begin
+    if item.Key.EndsWith('_dbg_inf') then
+      Continue;
+    try
+      PropInfo := GetPropInfo(PTypeInfo(AObject.ClassInfo), item.Key);
+      if PropInfo = nil then
+        Continue;
+      PropName := string(PropInfo^.PropType^.Name);
+      if item.IsObject then
+      begin
+        if PropInfo^.PropType^.Kind = tkClass then
+          item.SetObjectProp(TObject({$IFDEF CPUX64}Int64{$ELSE}Integer{$ENDIF}(GetPropValue(AObject, item.Key))));
+      end
+      else
+      begin
+        if PropName = 'TAlphaColor' then
+          SetPropValue(AObject, item.Key, {$IFDEF CPUX64}StrToInt64{$ELSE}StrToUInt{$ENDIF}('$' + item.ToStr.Substring(1, 8)))
+        else
+          SetPropValue(AObject, item.Key, item.ToStr);
+      end;
+    except
+      on E: Exception do
+        if ARaiseExc then
+          raise Exception.Create(E.Message);
+    end;
+  end;
+end;
+
+{ TRJSON }
+{ ============================================================================ }
+
+end.