-
Notifications
You must be signed in to change notification settings - Fork 22
/
qjson.pas
7109 lines (6754 loc) · 198 KB
/
qjson.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
unit qjson;
{$I 'qdac.inc'}
interface
{
本源码来自QDAC项目,版权归swish(QQ:109867294)所有。
(1)、使用许可及限制
您可以自由复制、分发、修改本源码,但您的修改应该反馈给作者,并允许作者在必要时,
合并到本项目中以供使用,合并后的源码同样遵循QDAC版权声明限制。
您的产品的关于中,应包含以下的版本声明:
本产品使用的JSON解析器来自QDAC项目中的QJSON,版权归作者所有。
(2)、技术支持
有技术问题,您可以加入QDAC官方QQ群250530692共同探讨。
(3)、赞助
您可以自由使用本源码而不需要支付任何费用。如果您觉得本源码对您有帮助,您可以赞
助本项目(非强制),以使作者不为生活所迫,有更多的精力为您呈现更好的作品:
赞助方式:
支付宝: [email protected] 姓名:管耸寰
建设银行:
户名:管耸寰
账号:4367 4209 4324 0179 731
开户行:建设银行长春团风储蓄所
}
{ 修订日志
2017.4.10
==========
+ 修正了大浮点数判断时出错的问题(SeMain报告)
2017.1.1
==========
+ 增加 Insert 系列函数,用于在指定的位置插入一个结点(阿木、恢弘建议)
2016.11.23
==========
+ 增加 Reset 函数,重置结点的所有内容,同时也会从父结点中移除自身(不得闲报告)
2016.11.16
==========
* 修正了 FromRtti 时,对象只读或只写的属性保存,造成ToRtti恢复属性值时无法处理的问题(KEN报告)
2016.11.02
==========
* 修正了 Merge 合并时,参数为 Ignore 时未考虑子元素不同的问题,造成子元素没有合并的问题(阿木报告)
2016.8.23
==========
* 修正了 ToFixedArray 没有比较数组元素个数与子结点数的造成可能访问越界的问题(随云报告)
2016.6.24
==========
* 修正了 JsonCat 时分配内存空间可能不足的问题(熊猫叔叔报告)
* 修改对 encddecd 单元的引用(恢弘建议)
2016.2.24
==========
* 修正了 ItemByPath 时对特殊数组路径解析存在的问题(恢弘报告)
2016.1.25
==========
* 修正了 TQHashedJson 在析构时,提前释放了 FHashTable 的问题
2016.1.23
==========
* 修改 SetValue 函数自动检测类型的代码,避免抛出异常
2016.1.11
==========
+ 新增 Equals 函数判定两个 JSON 内容是否相同
+ 新增 CommentStyle 和 Comment 属性来管理备注
+ 新增 Merge/Intersect/Diff 函数来进行内容的合并、求重和差异比较
2015.11.19
===========
* 修正了TQHashedJson添加结点时,没有正确对名称进行哈希的问题(QQ报告)
* 修正了 FromRtti 在 Win64 时,如果没有子属性时出错的问题
2015.10.30
===========
* 修正了TQJsonStreamHelper.EndArray时,JSON编码可能出错的问题(中国制造报告)
2015.10.23
===========
+ ForceName 确保指定的名称存在
2015.10.20
==========
+ Exists 函数判断是否包含指定路径的子结点(阿木建议)
+ Sort 排序子结点(恢弘建议)
+ ExchangeOrder 交换两个结点
+ ContainsName 判断是否有指定名称的子结点
+ ContainsValue 判断是否包含指定值的子结点(青春建议)
2015.10.8
==========
* 修正了名称末尾包含空格时错误的自动移除的问题
2015.9.22
==========
* 修正了TQJsonStreamHelper写入对象或数组时,如果没有子元素时造成格式错乱的问题
2015.7.20
==========
* 更改了 ToRtti 时对采用默认值对象属性的控制方法
* 修正了 ToRtti 和 FromRtti 落下 TDateTime 类型属性的问题(感谢好人一生平安)
2015.6.23
=========
+ 新增 TQJsonStreamHelper 用于直接向流中写入 JSON 格式的数据,优点是由于省略了
TQJson对象的创建和维速度更快,缺点是需要自行控制更多的步骤(如数组和对象的闭合)
2015.5.22
=========
+ 增加二进制编码为Base64的默认启用函数 EncodeJsonBinaryAsBase64,如果要恢复默认,则用
EncodeJsonBinaryAsHex
2015.4.21
=========
+ IgnoreCase 属性用于控制Json对象自身的区分大小写属性,默认继承自全局的JsonCaseSensitive属性值(恢弘建议)
+ Root 属性用于返回根结点
* HashName 从 TQHashedJson 移到 TQJson
2015.2.6
=========
* 修正了 AsInteger/AsFloat 时不支持十六进制的问题
2015.2.1
=========
* 修改了解析和编码的行为,兼容名称为空字符串的JSON代码(感谢 Synopse)
2015.1.26
==========
+ 增加无参数的Delete函数用于删除结点自身
2015.1.19
==========
* 公开了编码时的一些特定字符为变量,这样方便程序员手动自己控制编码的结果以增加可读性(阿木建议)
* 修正了重复调用 FromRtti 时没有清空末次值的问题(阿木报告)
2015.1.13
==========
* 修正了 TQHashedJson 的 IndexOf 未正确处理大小写的问题
* 修正了 TQHashedJson 在解析完成后未正确重新计算哈希值的问题(阿木报告)
* 修正了解析数值类型时 Parse 时未正确移除名称空格的问题
2015.1.6
========
* 修正了SetAsVariant对非标准的变体类型的支持问题(小枫报告)
2015.1.5
=========
* 修正了IsChildOf的一处判断错误,造成可能发生AV异常
2015.1.4
=========
* 修改了 ItemByName 的部分代码,修正了没有正确处理 JsonCaseSensitive 标记的问题,造成忽略大小写无效的问题(阿木)
* 修正了 ItemByName 对数组下标处理的逻辑错误
* 修正了 ItemByPath 不支持多维数组的问题
2015.1.3
=========
* SaveToStream/SaveToFile 增加了一个ADoFormat参数,以便控制是否格式化(恢弘、阿木建议)
2014.12.24
==========
* 修正了解析Json中包含注释时,处理不够全面的问题(kylix2008报告)
2014.12.13
==========
+ 增加HasChild函数,来判定指定路径的子结点是否存在(阿木建议)
2014.11.25
==========
* 修改了ItemByPath的代码,支持按索引顺序来访问jdtObject类型的子成员
2014.11.24
==========
* 修正了ToRtti.FoArray的行为,在子类型名字未找到时,提示异常
2014.11.20
==========
+ 加入AsBytes属性,以支持二进制数据类型,默认实现直接使用的十六进制字符串表达,
上层可以重载OnQJsonEncodeBytes和OnQJsonDecodeBytes事件来替换为自己的实现(如
ZLib+Base64)
+ 加入ValueFromStream/ValueFromFile/StreamFromValue/StreamFromFile函数
2014.11.14
==========
* 修正了GetAsVariant时没有处理jdtNull类型的问题
2014.11.13
==========
+ 加入IsBool属性判断当前值是否能转换为布尔值访问(五月光_???-建议)
2014.11.10
==========
* 修正了FromRtti/ToRtti在处理TCollection类型时存在的问题(阿木报告)
* 修正了FromRtti中ToObject子函数的一处错误(hq200306报告)
2014.11.6
==========
* 修正了FromRtti时集合类型元素添加结点时,AName误写成Name造成结点未命名的问题(阿木报告)
+ 新增IntByPath,IntByName,BoolByPath,BoolByName,FloatByPath,FloatByName,DateTimeByPath,
DateTimeByName函数,以简化判断编程(FreeSpace8建议)
2014.10.30
==========
+ 新增Detach、AttachTo、MoveTo、Remove函数
* 允许Json结点名称重命名以避免调用MoveTo、AttachTo时元素未命名
2014.9.11
=========
* 修正了从流或文件中加载空白JSON数组和对象时出错的问题(恢弘报告)
* 修改直接将非对象或数组值保存到流中的策略(麦子仲肥报告):
1、如果JSON结点的名称已经指定,则保存为对象的一个子对象;
2、如果未指定名称,且类型为未知或为jdtNull,则不保存任何内容
2014.9.3
=========
* 修正了解析特殊字符串值时可能丢失字符的问题(阿木报告)
2014.8.27
=========
* 修正了解析数组或对象结束符前有行注释时出错的问题(阿木报告)
2014.8.15
=========
* 修正了Add函数自动检测内容类型时,添加特定格式如11,23时解析出错的问题(Tuesday报告)
2014.7.31
=========
* 修正了解析出错时,如果行过长,系统异常无法完整显示的问题(音儿小白报告)
* 修正了解析时可能陷入死循环的问题(音儿小白报告)
* 修正了出现异常时,异常行提示重复的问题
* 修正了ForcePath时'array[].subobjectname'未正确生成路径的问题(音儿小白报告)
2014.7.28
=========
* 修正了ToRtti如果源类型是日期时间类型,而JSON为null时解析出错的问题(恢宏报告)
* 修改ToRecord参数类型为var,而不是const(恢宏报告)
2014.7.16
=========
* 修正了GetPath时,未初始化结果字符串造成Path属性可能出错的问题(音儿小白报告)
2014.7.6
=========
+ ToRtti加入对静态数组类型的支持
2014.7.3
=========
* 修正了Assign时复制了当前结点名称的问题
2014.7.1
=========
* AsString修改jdtNull/jdtUnknown时,改为返回空字符串
2014.6.28
=========
* 修正了ForcePath('Items[]')默认添加了空子结点的问题(pony,光明报告)
+ 加入JsonRttiEnumAsInt全局选项,控制枚举值和集合值是否保存成其字符串表达,默认为True(恢弘建议)
2014.6.27
=========
+ 增加TryParse函数(恢弘建议)
* 修改了Encode时将自己的名称也加到了结果字符串中的问题(恢弘报告)
* 修正了FromRTTI时,对于方法、事件等属性没有进行过滤的问题
* 修正了ToRtti.ToArray时,对于动态数组的设置长度时类型错误(恢弘报告)
2014.6.26
==========
* 修正了ToRtti.ToRecord子函数处理日期类型时的错误(感谢群友恢弘大量的RTTI建议和测试)
* 加入HPPEMIT默认链接本单元(麦子仲肥 建议)
2014.6.23
==========
+ FromRecord支持动态数组和普通数组
2014.6.21
==========
* 移除原来AddObject/AddRecord/ToObject/ToRecord支持
+ 添加FromRtti/ToRtti/FromRecord/ToRecord/ToRttiValue函数支持,替换原来的RTTI函数
+ 添加Invoke函数,支持直接通过Json调用对应的函数,具体参考Demo
2014.6.17
=========
* AsFloat赋值时加入对Nan、Infinite、NegInfinite三个无效值的检查
* AsVariant赋值时加入对varNull,varEmpty,varUnknown,varUInt64类型的支持
2014.5.27
==========
+ TQHashedJson 支持,这是一个面向查询优化的版本,使用哈希表加快ItemByName的查询速度,
如果您的应用中大量的使用ItemByName、ItemByPath等查询,使用它代替TQJson,否则应直接
使用TQJson
2014.5.14
=========
+ 加入CopyIf/DeleteIf/FindIf函数
+ 加入for..in语法支持
* 修正了Encode和ForcePath可能存在的问题
2014.5.6
========
+ 加入ParseBlock函数,以支持流式传送分段解析
* 修正了解析\uxxxx时的识别错误
* 修改Parse函数为清除已有子结点
2014.5.4
========
+ 加入对JavaScript和.net的日期时间类型/DATE(MillSeconds+TimeZone)/格式的支持
* Json数据支持加入VCL的TDateTime类型支持,生成的JSON数据默认由JsonDateFormat、
JsonTimeFormat,JsonDateTimeFormat三个变量控制,如果StrictJson变量为True,则
生成/DATE(MillSeconds+TimeZone)/格式
【注】
日期时间类型仅适用于运行时,生成JSON后实际上仍为字符串,这种字符串再次打开时
将丢失类型信息,但您仍可以直接以AsDateTime属性来读写。如果日期时间类型使用
JavaScript和.net格式并且包含了时区信息,则时间将被转换为格林威治时间。
2014.5.1
========
+ 加入AddRecord函数,支持直接保存记录数据,但以下类型的成员会被忽略
对象(Class)、函数(Method)、接口(Interface)、类型引用(ClassRef),指针(Pointer)、过程(Procedure)
将来可能根据实际需要决定是否添加支持
+ 加入ToRecord函数,完成Json直接到记录类型的转换
+ 加入Copy函数用于创建当前结点的一个拷贝实例,注意目前版本克隆内部调用了Copy函数,将来可能改掉
* 修正了Assign函数的一处错误
}
// 测试环境仅为Delphi 2007或XE6,其它版本的开发环境,请自行修改
uses classes, sysutils, math, qstring, typinfo, qrbtree,
{$IF RTLVersion>27}
System.NetEncoding{$ELSE}EncdDecd{$IFEND}
{$IFDEF MSWINDOWS}, windows{$ENDIF}
{$IFDEF UNICODE}, Generics.Collections{$ENDIF}{$IF RTLVersion>=21},
Rtti{$IFEND >=XE10}
{$IF RTLVersion<22}// 2007-2010
{$IFDEF ENABLE_REGEX}
, PerlRegEx, pcre
{$ENDIF}
{$ELSE}
, RegularExpressionsCore
{$IFEND}
;
{$M+}
{$HPPEMIT '#pragma link "qjson"'}
{$IF RTLVersion<=27}
{$HPPEMIT '#pragma link "soaprtl.lib"'}
{$IFEND}
// 如果要使用类名来表示方式,如TForm1.FormCreate,则启用下面的定义,否则方法名为Form1.FormCreate
{ .$DEFINE TYPENAMEASMETHODPREF }
type
/// 本单元是QDAC的组成部分,受QDAC授权限制,详情访问QDAC网站了解
/// <summary>
/// JSON解析单元,用于快速解析和维护JSON结构.全局变量StrictJson为False时,支持
/// 注释和名称不包含'"'。
/// </summary>
/// TQJsonDataType用于记录JSON数据元的类型,可取值包括:
/// <list>
/// <item>
/// <term>jdtUnknown</term><description>未知类型,只有新构造对象未赋值时,才会是该类型</description>
/// </item>
/// <item>
/// <term>jdtNull</term><description>NULL</description>
/// </item>
/// <item>
/// <term>jdtString</term><description>字符串</description>
/// </item>
/// <item>
/// <term>jdtInteger</term><description>整数(Int64,不管整数值多大,内部均使用64位整数存贮)</description>
/// </item>
/// <item>
/// <term>jdtFloat</term><description>双精度浮点数(Double)</description>
/// </item>
/// <item>
/// <term>jdtBoolean</term><description>布尔</description>
/// </item>
/// <item>
/// <term>jdtDateTime</term><description>日期时间类型</description>
/// </item>
/// <item>
/// <term>jdtArray</term><description>数组</description>
/// </item>
/// <item>
/// <term>jdtObject</term><description>对象</description>
/// </item>
/// </list>
TQJsonDataType = (jdtUnknown, jdtNull, jdtString, jdtInteger, jdtFloat,
jdtBoolean, jdtDateTime, jdtArray, jdtObject);
TQJson = class;
{$IF RTLVersion>=21}
/// <summary>
/// RTTI信息过滤回调函数,在XE6上支持匿名函数,在XE及以前的版本采用事件回调
/// </summary>
/// <param name="ASender">触发事件的TQJson对象</param>
/// <param name="AName">属性名(AddObject)或字段名(AddRecord)</param>
/// <param name="AType">属性或字段的类型信息</param>
/// <param name="Accept">是否记录该属性或字段</param>
/// <param name="ATag">用户自定义的附加数据成员</param>
TQJsonRttiFilterEventA = reference to procedure(ASender: TQJson;
AObject: Pointer; AName: QStringW; AType: PTypeInfo; var Accept: Boolean;
ATag: Pointer);
/// <summary>
/// 结点过滤处理函数,以在XE6上支持匿名函数
/// </summary>
/// <param name="ASender">触发事件的TQJson对象</param>
/// <param name="AItem">要过滤的对象</param>
/// <param name="Accept">是否要处理该对象</param>
/// <param name="ATag">用户附加的数据项</param>
TQJsonFilterEventA = reference to procedure(ASender, AItem: TQJson;
var Accept: Boolean; ATag: Pointer);
{$IFEND >=2010}
/// <summary>
/// RTTI信息过滤回调函数,在XE6上支持匿名函数,在XE及以前的版本采用事件回调
/// </summary>
/// <param name="ASender">触发事件的TQJson对象</param>
/// <param name="AName">属性名(AddObject)或字段名(AddRecord)</param>
/// <param name="AType">属性或字段的类型信息</param>
/// <param name="Accept">是否记录该属性或字段</param>
/// <param name="ATag">用户自定义的附加数据成员</param>
TQJsonRttiFilterEvent = procedure(ASender: TQJson; AObject: Pointer;
AName: QStringW; AType: PTypeInfo; var Accept: Boolean; ATag: Pointer)
of object;
/// <summary>
/// 结点过滤处理函数,以在XE6上支持匿名函数
/// </summary>
/// <param name="ASender">触发事件的TQJson对象</param>
/// <param name="AItem">要过滤的对象</param>
/// <param name="Accept">是否要处理该对象</param>
/// <param name="ATag">用户附加的数据项</param>
TQJsonFilterEvent = procedure(ASender, AItem: TQJson; var Accept: Boolean;
ATag: Pointer) of object;
PQJson = ^TQJson;
{$IF RTLVersion>=21}
TQJsonItemList = TList<TQJson>;
{$ELSE}
TQJsonItemList = TList;
{$IFEND}
/// <summary>
/// TQJsonTagType用于内部AddObject和AddRecord函数的内部过滤使用
/// </summary>
/// <list>
/// <item>
/// <term>ttAnonEvent</term><description>回调匿名函数</description>
/// <term>ttNameFilter</term><description>属性或成员名称过滤</descriptio>
/// </list>
TQJsonTagType = (ttAnonEvent, ttNameFilter);
PQJsonInternalTagData = ^TQJsonInternalTagData;
/// <summary>
/// TQJsonInternalTagData用于AddRecord和AddObject函数需要内部过滤RTTI信息时使用
/// </summary>
TQJsonInternalTagData = record
/// <summary>Tag数据的类型</summary>
TagType: TQJsonTagType;
{$IF RTLVersion>=21}
/// <summary>过滤使用的匿名函数</summary>
OnEvent: TQJsonRttiFilterEventA;
{$IFEND >=2010}
/// <summary>接受的属性(AddObject)或记录字段(AddRecord)名称,如果名称同时在IgnoreNames出现,则IgnoreNames里的信息被忽略</summary>
AcceptNames: QStringW;
/// <summary>忽略的属性(AddObject)或记录字段(AddRecord)名称,如果名称同时在AcceptNameds里,则AcceptNames优先</summary>
IgnoreNames: QStringW;
/// <summary>原始传递给AddObject或AddRecord的附加数据成员,它将被传递给OnEvent的Tag,以供用户使用</summary>
Tag: Pointer;
end;
TQJsonEnumerator = class;
/// <summary>用于外部支持对象池的函数,创建一个新的QJSON对象,注意从池中创建的对象</summary>
/// <returns>返回新创建的QJSON对象</returns>
TQJsonCreateEvent = function: TQJson;
/// <summary>用于外部将对象缓存,以便重用对象</summary>
/// <param name="AJson">要释放的Json对象</param>
TQJsonFreeEvent = procedure(AJson: TQJson);
TQJsonEncodeBytesEvent = procedure(const ABytes: TBytes;
var AResult: QStringW);
TQJsonDecodeBytesEvent = procedure(const S: QStringW; var AResult: TBytes);
EJsonError = class(Exception)
end;
// </summary>
/// TQJsonCommentStyle 用于记录JSON中注释的类型,注意,严格模式下,注释将被忽略
/// <list>
/// <item>
/// <term>jcsIgnore</term><description>保存时忽略原来的注释</description>
/// <term>jcsInherited</term><description>保存时由父结点的设置决定注释出现的位置</description>
/// <term>jcsBeforeName</term><description>在项目名之前进行注释</description>
/// <term>jcsAfterValue</term><description>在项目值之后,逗号分隔符前进行注释</description>
/// </item>
TQJsonCommentStyle = (jcsIgnore, jcsInherited, jcsBeforeName, jcsAfterValue);
TQJsonMergeMethod = (jmmIgnore, jmmAsSource, jmmAppend, jmmReplace);
// TQJsonSortFlags=(jsmAsString,);
/// <summary>
/// TQJson用于解析并维护JSON格式的对象类型,要使用前,需要先在堆中创建对应的实例。
/// TQJson和TQXML在绝大多数接口上保持一致,但由于Json是有类型信息,而XML没有类型
/// 信息(始终认为是字符串),因此少部分接口会略有不同.
/// 与其它实现不同,QJSON所有的类型都是同一个对象实现,根据DataType的不同,而使用
/// 不同的成员来访问。当类型为jdtArray或者是jdtObject时,它可以有子结点.
/// </summary>
TQJson = class
private
function GetRoot: TQJson;
protected
FName: QStringW;
FNameHash: Cardinal;
FDataType: TQJsonDataType;
FValue: QStringW;
FComment: QStringW;
FCommentStyle: TQJsonCommentStyle;
FParent: TQJson;
FData: Pointer;
FItems: TQJsonItemList;
FIgnoreCase: Boolean;
function GetValue: QStringW;
procedure SetValue(const Value: QStringW);
procedure SetDataType(const Value: TQJsonDataType);
function TryGetAsBoolean(var AValue: Boolean): Boolean;
function GetAsBoolean: Boolean;
function TryGetAsFloat(var AValue: Extended): Boolean;
function GetAsFloat: Extended;
function TryGetAsInt64(var AValue: Int64): Boolean;
function GetAsInt64: Int64;
function GetAsInteger: Integer;
function GetAsString: QStringW;
function GetAsObject: QStringW;
procedure SetAsObject(const Value: QStringW);
function TryGetAsDateTime(var AValue: TDateTime): Boolean;
function GetAsDateTime: TDateTime;
procedure SetAsBoolean(const Value: Boolean);
procedure SetAsFloat(const Value: Extended);
procedure SetAsInt64(const Value: Int64);
procedure SetAsInteger(const Value: Integer);
procedure SetAsString(const Value: QStringW);
procedure SetAsDateTime(const Value: TDateTime);
function GetCount: Integer;
function GetItems(AIndex: Integer): TQJson;
class function CharUnescape(var p: PQCharW): QCharW;
class function CharEscape(c: QCharW; pd: PQCharW): Integer;
procedure ArrayNeeded(ANewType: TQJsonDataType);
procedure ValidArray;
procedure ParseObject(var p: PQCharW);
function ParseJsonPair(ABuilder: TQStringCatHelperW;
var p: PQCharW): Integer;
function ParseName(ABuilder: TQStringCatHelperW; var p: PQCharW): Integer;
procedure ParseValue(ABuilder: TQStringCatHelperW; var p: PQCharW);
function FormatParseError(ACode: Integer; AMsg: QStringW; ps, p: PQCharW)
: QStringW;
procedure RaiseParseException(ACode: Integer; ps, p: PQCharW);
function TryParseValue(ABuilder: TQStringCatHelperW;
var p: PQCharW): Integer;
function BooleanToStr(const b: Boolean): QStringW;
function GetIsNull: Boolean;
function GetIsNumeric: Boolean;
function GetIsArray: Boolean;
function GetIsObject: Boolean;
function GetIsString: Boolean;
function GetIsDateTime: Boolean;
function GetAsArray: QStringW;
procedure SetAsArray(const Value: QStringW);
function GetPath: QStringW;
function GetAsVariant: Variant;
procedure SetAsVariant(const Value: Variant);
function GetAsJson: QStringW;
procedure SetAsJson(const Value: QStringW);
function GetItemIndex: Integer;
function ParseJsonTime(p: PQCharW; var ATime: TDateTime): Boolean;
function CreateJson: TQJson; virtual;
procedure FreeJson(AJson: TQJson); inline;
procedure CopyValue(ASource: TQJson); inline;
procedure Replace(AIndex: Integer; ANewItem: TQJson); virtual;
procedure InternalRttiFilter(ASender: TQJson; AObject: Pointer;
APropName: QStringW; APropType: PTypeInfo; var Accept: Boolean;
ATag: Pointer);
function InternalEncode(ABuilder: TQStringCatHelperW; ADoFormat: Boolean;
ADoEscape: Boolean; ANullConvert: Boolean; const AIndent: QStringW)
: TQStringCatHelperW;
function ArrayItemTypeName(ATypeName: QStringW): QStringW;
function ArrayItemType(ArrType: PTypeInfo): PTypeInfo;
procedure DoJsonNameChanged(AJson: TQJson); virtual;
procedure SetName(const Value: QStringW);
function GetIsBool: Boolean;
function GetAsBytes: TBytes;
procedure SetAsBytes(const Value: TBytes);
class function SkipSpaceAndComment(var p: PQCharW;
var AComment: QStringW): Integer;
procedure DoParsed; virtual;
procedure SetIgnoreCase(const Value: Boolean);
function HashName(const S: QStringW): TQHashType;
procedure HashNeeded; inline;
procedure FromType(const AValue: String; AType: TQJsonDataType);
public
/// <summary>构造函数</summary>
constructor Create; overload;
constructor Create(const AName, AValue: QStringW;
ADataType: TQJsonDataType = jdtUnknown); overload;
/// <summary>析构函数</summary>
destructor Destroy; override;
{ <summary>添加一个子结点</summary>
<param name="ANode">要添加的结点</param>
<returns>返回添加的结点索引</returns>
}
function Add(ANode: TQJson): Integer; overload;
/// <summary>添加一个未命名的JSON子结点</summary>
/// <returns>返回添加的结点实例</returns>
/// <remarks>
/// 一般情况下,除非数组类型,不应添加未命名的实例
/// </remarks>
function Add: TQJson; overload;
/// <summary>添加一个数组</summary>
/// <param name="AName">要添加的对象的结点名称</param>
/// <param name="AValue">要添加的内容的值表达式(字符串)</param>
/// <param name="ADataType">表达式类型,如果为jdtUnknown,则会自动检测内容类型</param>
/// <returns>返回创建的结点索引</returns>
function Add(AName, AValue: QStringW;
ADataType: TQJsonDataType = jdtUnknown): Integer; overload;
/// <summary>添加一个数组</summary>
/// <param name="AName">要添加的对象的结点名称</param>
/// <param name="AItems">要添加的数组内容</param>
/// <returns>返回创建的结点实例</returns>
function Add(const AName: QStringW; AItems: array of const)
: TQJson; overload;
{ <summary>添加一个子结点</summary>
<param name="AName">要添加的结点名</param>
<param name="ADataType">要添加的结点数据类型,如果省略,则自动根据值的内容检测</param>
<returns>返回添加的新对象</returns>
<remarks>
1.如果当前类型不是jdtObject或jdtArray,将自动转换为jdtObject类型
2.上层应自己负责重名检查
</remarks>
}
function Add(AName: QStringW; ADataType: TQJsonDataType): TQJson; overload;
/// <summary>添加一个子结点</summary>
/// <param name="AName">要添加的结点名,如果当前结点为数组,则在输出时会忽略该值</param>
/// <param name="AValue">要添加的结点值</param>
/// <returns>返回添加的新对象</returns>
function Add(AName: QStringW; AValue: Extended): TQJson; overload;
/// <summary>添加一个子结点</summary>
/// <param name="AName">要添加的结点名,如果当前结点为数组,则在输出时会忽略该值</param>
/// <param name="AValue">要添加的结点值</param>
/// <returns>返回添加的新对象</returns>
function Add(AName: QStringW; AValue: Int64): TQJson; overload;
/// <summary>添加一个子结点</summary>
/// <param name="AName">要添加的结点名,如果当前结点为数组,则在输出时会忽略该值</param>
/// <param name="AValue">要添加的结点值</param>
/// <returns>返回添加的新对象</returns>
function Add(AName: QStringW; AValue: Boolean): TQJson; overload;
/// <summary>添加一个子结点</summary>
/// <param name="AName">要添加的结点名,如果当前结点为数组,则在输出时会忽略该值</param>
/// <param name="AValue">要添加的子结点</param>
/// <returns>返回添加的新对象的索引位置</returns>
/// <remarks>添加的结点释放归主本结点负责,外部不应再释放</remarks>
function Add(AName: QStringW; AChild: TQJson): Integer; overload;
/// <summary>添加一个数组类型子结点</summary>
/// <param name="AName">要添加的结点名,如果当前结点为数组,则在输出时会忽略该值</param>
/// <returns>返回添加的新对象</returns>
function AddArray(AName: QStringW): TQJson; overload;
/// <summary>添加一个子结点</summary>
/// <param name="AName">要添加的结点名,如果当前结点为数组,则在输出时会忽略该值</param>
/// <param name="AValue">要添加的结点值</param>
/// <returns>返回添加的新对象</returns>
function AddDateTime(AName: QStringW; AValue: TDateTime): TQJson; overload;
/// <summary>添加一个子结点</summary>
/// <param name="AName">要添加的结点名,如果当前结点为数组,则在输出时会忽略该值</param>
/// <param name="AValue">要添加的结点值</param>
/// <returns>返回添加的新对象</returns>
function AddVariant(AName: QStringW; AValue: Variant): TQJson; overload;
/// <summary>添加一个子结点(Null)</summary>
/// <param name="AName">要添加的结点名,如果当前结点为数组,则在输出时会忽略该值</param>
/// <returns>返回添加的新对象</returns>
function Add(AName: QStringW): TQJson; overload; virtual;
function Insert(AIndex: Integer; const AName: String): TQJson; overload;
function Insert(AIndex: Integer; const AName: String;
ADataType: TQJsonDataType): TQJson; overload;
function Insert(AIndex: Integer; const AName, AValue: String;
ADataType: TQJsonDataType = jdtString): TQJson; overload;
function Insert(AIndex: Integer; const AName: String; AValue: Extended)
: TQJson; overload;
function Insert(AIndex: Integer; const AName: String; AValue: Int64)
: TQJson; overload;
function Insert(AIndex: Integer; const AName: String; AValue: Boolean)
: TQJson; overload;
procedure Insert(AIndex: Integer; AChild: TQJson); overload;
/// <summary>强制一个路径存在,如果不存在,则依次创建需要的结点(jdtObject或jdtArray)</summary>
/// <param name="APath">要添加的结点路径</param>
/// <returns>返回路径对应的对象</returns>
/// <remarks>
/// 假设以下路径完全不存在,则ForcePath会按如下规则执行:
/// 1、如果APath中包含[],则认为对应的路径结点为数组,示例如下:
/// (1)、'a.b[].name':
/// a -> jdtObject
/// b -> jdtArray
/// b[0].name -> jdtNull(b的索引未指定,自动认为是b[0]
/// (2)、'a.c[2].name':
/// a -> jdtObject
/// c -> jdtArray
/// c[2].name -> jdtNull
/// 其中,c[0],c[1]被自动创建,并赋值为jdtNull,执行完成后c为包含三个元素的数组
/// (3)、'a[0]':
/// a -> jdtArray
/// a[0] -> jdtNull
/// 2、路径分隔符./\是等价的,并且结点名称中不应包含上述三个字符之一,即:
/// a.b.c和a\b\c和a/b/c是完全相同的路径
/// 3、如果APath指定的对象类型不匹配,则会抛出异常,如a为对象,但使用a[0].b访问时。
/// </remarks>
function ForcePath(APath: QStringW): TQJson;
/// <summary>确保指定名称的子结点存在</summary>
/// <param name="AName">子结点名称</param>
/// <param name="AType">子结点的类型</param>
/// <returns>如果子结点存在,则返回指定的子结点,如果不存在,则添加子结点并返回</returns>
/// <remarks>与 Add 函数的区别在于它会检查子结点是否存在,以避免重复,而 Add 不检查。与ForcePath的区别在于不会识别名称中的特殊字符,从而允许创建特殊的名称</remarks>
function ForceName(AName: QStringW;
AType: TQJsonDataType = jdtNull): TQJson;
/// <summary>解析指定的JSON字符串</summary>
/// <param name="p">要解析的字符串</param>
/// <param name="l">字符串长度,<=0认为是以\0(#0)结尾的C语言标准字符串</param>
/// <remarks>如果l>=0,会检测p[l]是否为\0,如果不为\0,则会创建拷贝实例并解析拷贝实例</remarks>
procedure Parse(p: PWideChar; l: Integer = -1); overload;
/// <summary>解析指定的JSON字符串</summary>
/// <param name="s">要解析的JSON字符串</param>
procedure Parse(const S: QStringW); overload;
function TryParse(p: PWideChar; l: Integer = -1): Boolean; overload;
/// <summary>解析指定的JSON字符串</summary>
/// <param name="s">要解析的JSON字符串</param>
function TryParse(const S: QStringW): Boolean; overload;
/// <summmary>从流中解析首个JSON数据块</summary>
/// <param name="AStream">流对象</param>
/// <param name="AEncoding">流数据的编码方式</param>
/// <remarks>ParseBlock适合解析分段式JSON,它会从当前位置开始,解析到当前对象结束为止.
/// 可以很好的满足渐进式传输的需要</remarks>
procedure ParseBlock(AStream: TStream; AEncoding: TTextEncoding);
/// <summary>拷贝生成一个新的实例</summary>
/// <returns>返回新的拷贝实例</returns>
/// <remarks>因为是拷贝,所以新旧对象之间的内容变更没有任何关系,更改任意一个
/// 对象,不会对另外一个对象造成影响。
/// </remarks>
function Copy: TQJson;
{$IF RTLVersion>=21}
/// <summary>拷贝生成一个新的实例</summary>
/// <param name="ATag">用户附加的标签数据</param>
/// <param name="AFilter">用户过滤事件,用于控制要拷贝的内容</param>
/// <returns>返回新的拷贝实例</returns>
/// <remarks>因为是拷贝,所以新旧对象之间的内容变更没有任何关系,更改任意一个
/// 对象,不会对另外一个对象造成影响。
/// </remarks>
function CopyIf(const ATag: Pointer; AFilter: TQJsonFilterEventA)
: TQJson; overload;
{$IFEND >=2010}
/// <summary>拷贝生成一个新的实例</summary>
/// <param name="ATag">用户附加的标签数据</param>
/// <param name="AFilter">用户过滤事件,用于控制要拷贝的内容</param>
/// <returns>返回新的拷贝实例</returns>
/// <remarks>因为是拷贝,所以新旧对象之间的内容变更没有任何关系,更改任意一个
/// 对象,不会对另外一个对象造成影响。
/// </remarks>
function CopyIf(const ATag: Pointer; AFilter: TQJsonFilterEvent)
: TQJson; overload;
/// <summary>克隆生成一个新的实例</summary>
/// <returns>返回新的拷贝实例</returns>
/// <remarks>因为实际上执行的是拷贝,所以新旧对象之间的内容变更没有任何关系,
/// 更改任意一个对象,不会对另外一个对象造成影响,但此行为将来并不保证,可能
/// 会调整为引用,以便相互影响。
/// </remarks>
function Clone: TQJson;
/// <summary>编码为字符串</summary>
/// <param name="ADoFormat">是否格式化字符串,以增加可读性</param>
/// <param name="ADoEscape">是否转义非字母和数字字符</param>
/// <param name="AIndent">ADoFormat参数为True时,缩进内容,默认为两个空格</param>
/// <returns>返回编码后的字符串</returns>
/// <remarks>AsJson等价于Encode(True,' ')</remarks>
function Encode(ADoFormat: Boolean; ADoEscape: Boolean = False;
AIndent: QStringW = ' '): QStringW;
/// <summary>获取指定名称获取结点的值的字符串表示</summary>
/// <param name="AName">结点名称</param>
/// <param name="ADefVal">默认值</param>
/// <returns>返回应结点的值</returns>
function ValueByName(AName, ADefVal: QStringW): QStringW;
/// <summary>获取指定名称获取结点的值的布尔值表示</summary>
/// <param name="AName">结点名称</param>
/// <param name="ADefVal">默认值</param>
/// <returns>返回应结点的值</returns>
function BoolByName(AName: QStringW; ADefVal: Boolean): Boolean;
/// <summary>获取指定名称获取结点的值的整数值表示</summary>
/// <param name="AName">结点名称</param>
/// <param name="ADefVal">默认值</param>
/// <returns>返回应结点的值</returns>
function IntByName(AName: QStringW; ADefVal: Int64): Int64;
/// <summary>获取指定名称获取结点的值的浮点值表示</summary>
/// <param name="AName">结点名称</param>
/// <param name="ADefVal">默认值</param>
/// <returns>返回应结点的值</returns>
function FloatByName(AName: QStringW; ADefVal: Extended): Extended;
/// <summary>获取指定名称获取结点的值的日期时间值表示</summary>
/// <param name="AName">结点名称</param>
/// <param name="ADefVal">默认值</param>
/// <returns>返回应结点的值</returns>
function DateTimeByName(AName: QStringW; ADefVal: TDateTime): TDateTime;
/// <summary>获取指定路径结点的值的字符串表示</summary>
/// <param name="AName">结点名称</param>
/// <param name="ADefVal">默认值</param>
/// <returns>如果结果不存在,返回默认值,否则,返回原始值</returns>
function ValueByPath(APath, ADefVal: QStringW): QStringW;
/// <summary>获取指定路径结点的值的布尔值表示</summary>
/// <param name="AName">结点名称</param>
/// <param name="ADefVal">默认值</param>
/// <returns>如果结果不存在,返回默认值,否则,返回原始值</returns>
function BoolByPath(APath: QStringW; ADefVal: Boolean): Boolean;
/// <summary>获取指定路径结点的值的整数表示</summary>
/// <param name="AName">结点名称</param>
/// <param name="ADefVal">默认值</param>
/// <returns>如果结果不存在,返回默认值,否则,返回原始值</returns>
function IntByPath(APath: QStringW; ADefVal: Int64): Int64;
/// <summary>获取指定路径结点的值的浮点数表示</summary>
/// <param name="AName">结点名称</param>
/// <param name="ADefVal">默认值</param>
/// <returns>如果结果不存在,返回默认值,否则,返回原始值</returns>
function FloatByPath(APath: QStringW; ADefVal: Extended): Extended;
/// <summary>获取指定路径结点的值的日期时间表示</summary>
/// <param name="AName">结点名称</param>
/// <param name="ADefVal">默认值</param>
/// <returns>如果结果不存在,返回默认值,否则,返回原始值</returns>
function DateTimeByPath(APath: QStringW; ADefVal: TDateTime): TDateTime;
/// <summary>获取指定路径结点的二进制表示</summary>
/// <param name="AName">结点名称</param>
/// <param name="ADefVal">默认值</param>
/// <returns>如果结果不存在,返回默认值,否则,返回原始值</returns>
function BytesByPath(APath: QStringW; ADefVal: TBytes): TBytes;
/// <summary>获取指定名称的第一个结点</summary>
/// <param name="AName">结点名称</param>
/// <returns>返回找到的结点,如果未找到,返回空(NULL/nil)</returns>
/// <remarks>注意QJson并不检查重名,因此,如果存在重名的结点,只会返回第一个结点</remarks>
function ItemByName(AName: QStringW): TQJson; overload; virtual;
/// <summary>获取指定名称的结点到列表中</summary>
/// <param name="AName">结点名称</param>
/// <param name="AList">用于保存结点的列表对象</param>
/// <param name="ANest">是否递归查找子结点</param>
/// <returns>返回找到的结点数量,如果未找到,返回0</returns>
/// <remarks>此函数不支持按数组下标方式检索</remarks>
function ItemByName(const AName: QStringW; AList: TQJsonItemList;
ANest: Boolean = False): Integer; overload;
{$IFDEF ENABLE_REGEX}
/// <summary>获取符合指定名称规则的结点到列表中</summary>
/// <param name="ARegex">正则表达式</param>
/// <param name="AList">用于保存结点的列表对象</param>
/// <param name="ANest">是否递归查找子结点</param>
/// <returns>返回找到的结点数量,如果未找到,返回0</returns>
function ItemByRegex(const ARegex: QStringW; AList: TQJsonItemList;
ANest: Boolean = False): Integer; overload;
{$ENDIF}
/// <summary>获取指定路径的JSON对象</summary>
/// <param name="APath">路径,以"."或"/"或"\"分隔</param>
/// <returns>返回找到的子结点,如果未找到返回NULL(nil)</returns>
/// <remarks>对于数组或对象子结点,可以直接使用下标来访问,多层的数组和对象子结点,可以使用[][]这样子来访问。</remarks>
function ItemByPath(APath: QStringW): TQJson;
/// <summary>从源对象复制JSON对象内容</summary>
/// <param name="ANode">要复制的源结点</param>
/// <remarks>注意不要复制子结点给自己,否则会造成死循环。要复制子结点,先复
/// 制一个子结点的新实例,再从新实例复制
/// </remarks>
procedure Assign(ANode: TQJson); virtual;
/// <summary>删除指定索引的子结点</summary>
/// <param name="AIndex">要删除的结点索引</param>
/// <remarks>
/// 如果指定索引的结点不存在,则抛出EOutRange异常
/// </remarks>
procedure Delete(AIndex: Integer); overload; virtual;
/// <summary>删除指定名称的子结点</summary>
/// <param name="AName">要删除的结点名称</param>
/// <remarks>
/// 如果要多个重名的结点,则只删除第一个
procedure Delete(AName: QStringW); overload;
/// <summary>从父结点中删除自身,如果没有父结点,则释放自己</summary>
procedure Delete; overload;
{$IF RTLVersion>=21}
/// <summary>
/// 删除符合条件的子结点
/// </summary>
/// <param name="ATag">用户自己附加的额外标记</param>
/// <param name="ANest">是否嵌套调用,如果为false,则只对当前子结点过滤</param>
/// <param name="AFilter">过滤回调函数,如果为nil,等价于Clear</param>
procedure DeleteIf(const ATag: Pointer; ANest: Boolean;
AFilter: TQJsonFilterEventA); overload;
{$IFEND >=2010}
/// <summary>
/// 删除符合条件的子结点
/// </summary>
/// <param name="ATag">用户自己附加的额外标记</param>
/// <param name="ANest">是否嵌套调用,如果为false,则只对当前子结点过滤</param>
/// <param name="AFilter">过滤回调函数,如果为nil,等价于Clear</param>
procedure DeleteIf(const ATag: Pointer; ANest: Boolean;
AFilter: TQJsonFilterEvent); overload;
/// <summary>查找指定名称的结点的索引</summary>
/// <param name="AName">要查找的结点名称</param>
/// <returns>返回索引值,未找到返回-1</returns>
function IndexOf(const AName: QStringW): Integer; virtual;
/// <summary>查找指定的值的结点索引</summary>
/// <param name="AValue">要查找的结点值</param>
/// <param name="AStrict">是否按严格模式比较</param>
/// <returns>返回索引值,未找到返回-1</returns>
function IndexOfValue(const AValue: Variant;
AStrict: Boolean = False): Integer;
{$IF RTLVersion>=21}
/// <summary>遍历结点查找符合条件的结点</summary>
/// <param name="ATag">用户自定义的附加额外标记</param>
/// <param name="ANest">是否嵌套调用,如果为false,则只对当前子结点过滤</param>
/// <param name="AFilter">过滤回调函数,如果为nil,则返回nil</param>
function FindIf(const ATag: Pointer; ANest: Boolean;
AFilter: TQJsonFilterEventA): TQJson; overload;
{$IFEND >=2010}
/// <summary>遍历结点查找符合条件的结点</summary>
/// <param name="ATag">用户自定义的附加额外标记</param>
/// <param name="ANest">是否嵌套调用,如果为false,则只对当前子结点过滤</param>
/// <param name="AFilter">过滤回调函数,如果为nil,则返回nil</param>
function FindIf(const ATag: Pointer; ANest: Boolean;
AFilter: TQJsonFilterEvent): TQJson; overload;
/// <summary>清除所有的结点</summary>
procedure Clear; virtual;
/// <summary>保存当前对象内容到流中</summary>
/// <param name="AStream">目标流对象</param>
/// <param name="AEncoding">编码格式</param>
/// <param name="AWriteBom">是否写入BOM</param>
/// <param name="ADoFormat">是否格式化Json结果</param>
/// <remarks>注意当前结点的名称不会被写入</remarks>
procedure SaveToStream(AStream: TStream; AEncoding: TTextEncoding = teUtf8;
AWriteBOM: Boolean = True; ADoFormat: Boolean = True);
/// <summary>从流的当前位置开始加载JSON对象</summary>
/// <param name="AStream">源数据流</param>
/// <param name="AEncoding">源文件编码,如果为teUnknown,则自动判断</param>
/// <remarks>流的当前位置到结束的长度必需大于2字节,否则无意义</remarks>
procedure LoadFromStream(AStream: TStream;
AEncoding: TTextEncoding = teUnknown);
/// <summary>保存当前对象内容到文件中</summary>
/// <param name="AFileName">文件名</param>
/// <param name="AEncoding">编码格式</param>
/// <param name="AWriteBOM">是否写入UTF-8的BOM</param>
/// <param name="ADoFormat">是否格式化Json结果</param>
/// <remarks>注意当前结点的名称不会被写入</remarks>
procedure SaveToFile(const AFileName: String;
AEncoding: TTextEncoding = teUtf8; AWriteBOM: Boolean = True;
ADoFormat: Boolean = True);
/// <summary>从指定的文件中加载当前对象</summary>
/// <param name="AFileName">要加载的文件名</param>
/// <param name="AEncoding">源文件编码,如果为teUnknown,则自动判断</param>
procedure LoadFromFile(const AFileName: String;
AEncoding: TTextEncoding = teUnknown);
/// / <summary>重置值为Null,等价于直接设置DataType为jdtNull</summary>
procedure ResetNull;
function Escape(const S: QStringW): QStringW;
/// <summary>重载TObject.ToString函数</summary>
function ToString: string; {$IFDEF UNICODE}override; {$ELSE}virtual;
{$ENDIF}
/// <summary>获取for..in需要的GetEnumerator支持</summary>
function GetEnumerator: TQJsonEnumerator;
/// <summary>判断自己是否是指定对象的子对象</summmary>
/// <param name="AParent">可能的父对象</param>
/// <returns>如果AParent是自己的父对象,返回True,否则返回false</returns>
function IsChildOf(AParent: TQJson): Boolean;
/// <summary>判断自己是否是指定对象的父对象</summmary>
/// <param name="AChild">可能的子对象</param>
/// <returns>如果AChild是自己的子对象,返回True,否则返回false</returns>
function IsParentOf(AChild: TQJson): Boolean;
{$IF RTLVersion>=21}
/// <summary>使用当前Json对象参数调用指定对象的相应函数</summary>
/// <param name="AInstance">函数所隶属的对象实例</param>
/// <returns>返回函数调用的结果</returns>
/// <remarks>函数名称为当前结点名称,函数的参数名称与子结点的名称要保持一致</remarks>
function Invoke(AInstance: TValue): TValue;
/// <summary>将当前的值转换为TValue类型的值</summary>
/// <returns>返回当前结点转换后的TValue值</returns>
function ToRttiValue: TValue;
/// <summary>从指定的RTTI实例中生成JSON数据</summary>
/// <param name="AInstance">对象或其它RTTI类型值</param>
/// <remarks>注意不是全部RTTI类型都受支持,如接口啥的</remarks>
procedure FromRtti(AInstance: TValue); overload;
/// <summary>将指定的来源地址按指定的类型生成JSON数据</summary>
/// <param name="ASource">对象或结构体地址</param>
/// <param name="AType">对象或结构体类型信息</param>
procedure FromRtti(ASource: Pointer; AType: PTypeInfo); overload;
/// <summary>从指定的记录实例中生成JSON数据</summary>
/// <param name="ARecord">记录实例</param>
procedure FromRecord<T>(const ARecord: T);
/// <summary>从当前JSON中还原到指定的对象实例中</summary>
/// <param name="AInstance">实例地址</param>
/// <param name="AClearCollections">是否在恢复TCollection对象的元素时先清理已有的元素,默认为trur</param>
/// <remarks>实际上参数只支持对象,记录由于目前无法直接转换为TValue,所以没
/// 意义,而其它类型因为是值拷贝,实际就算赋值了也返回不了,因此无意义</remarks>
procedure ToRtti(AInstance: TValue;
AClearCollections: Boolean = True); overload;
/// <summary>从当前JSON中按指定的类型信息还原到指定的地址</summary>
/// <param name="ADest">目的地址</param>
/// <param name="AType">对象或结构体的类型信息</param>
/// <param name="AClearCollections">是否在恢复TCollection对象的元素时先清理已有的元素,默认为trur</param>
/// <remarks>ADest对应的应是记录或对象,其它类型不受支持</remarks>
procedure ToRtti(ADest: Pointer; AType: PTypeInfo;
AClearCollections: Boolean = True); overload;
/// <summary>从当前的JSON中还原到指定的记录实例中</summary>
/// <param name="ARecord">目的记录实例</param>
/// <param name="AClearCollections">是否在恢复TCollection对象的元素时先清理已有的元素,默认为trur</param>
procedure ToRecord<T: record >(var ARecord: T;
AClearCollections: Boolean = True);
{$IFEND}
/// <summary>将指定索引的子结点移除</summary>
/// <param name="AItemIndex">要移除的子结点索引</param>
/// <returns>返回被移除的子结点,如果指定的索引不存在,返回nil</returns>
/// <remarks>被移除的子结点需要用户自己手工释放</remarks>
function Remove(AItemIndex: Integer): TQJson; overload; virtual;
/// <summary>将指定的子结点移除</summary>
/// <param name="AJson">要移除的子结点</param>
/// <remarks>被移除的子结点需要用户自己手工释放</remarks>
procedure Remove(AJson: TQJson); overload;
/// <summary>从当前父结点中分离当前结点</summary>
/// <remarks>分离后的结点需要单独释放</remarks>
procedure Detach;
/// <summary>将当前结点附加到新的父结点上</summary>
/// <param name="AParent">要附加的目标结点</param>
/// <remarks>附加后的结点由父结点负责释放</remarks>
procedure AttachTo(ANewParent: TQJson);
/// <summary>将当前结点移动的新的父结点的指定位置</summary>
/// <param name="ANewParent">新的父结点</param>
/// <param name="AIndex">新位置索引</param>
/// <remarks>如果新位置索引小于等于0,则插入到起始位置,如果大于父的已有结点数量,则插入到