utcjsoniterator.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861
  1. unit utcjsoniterator;
  2. {$mode objfpc}
  3. {$h+}
  4. {$modeswitch functionreferences}
  5. interface
  6. uses
  7. Classes, SysUtils, fpcunit, testregistry, System.JSON, System.JSON.Writers,
  8. System.JSON.Readers, System.JSON.Types, System.JSON.Builders, StreamEx;
  9. type
  10. TJSONIteratorTest = class(TTestCase)
  11. private
  12. FRewindCalled: Boolean;
  13. FIterateCount: Integer;
  14. function CreateReaderFromJSON(const aJSON: string): TJsonTextReader;
  15. procedure RewindCallback(aReader: TJsonReader);
  16. function IterateCallback(aIter: TJSONIterator): Boolean;
  17. published
  18. // Constructor tests
  19. procedure TestCreateWithReader;
  20. procedure TestCreateWithReaderAndRewindProc;
  21. // Public method tests
  22. procedure TestNext;
  23. procedure TestNextWithKey;
  24. procedure TestRewind;
  25. procedure TestRecurse;
  26. procedure TestReturn;
  27. procedure TestFind;
  28. procedure TestFindNestedPath;
  29. procedure TestIterate;
  30. procedure TestGetPathFromDepth;
  31. // Property tests - navigation
  32. procedure TestReaderProperty;
  33. procedure TestKeyProperty;
  34. procedure TestPathProperty;
  35. procedure TestTypeProperty;
  36. procedure TestParentTypeProperty;
  37. procedure TestIndexProperty;
  38. procedure TestInRecurseProperty;
  39. procedure TestDepthProperty;
  40. // Property tests - value accessors
  41. procedure TestAsString;
  42. procedure TestAsInteger;
  43. procedure TestAsInt64;
  44. procedure TestAsDouble;
  45. procedure TestAsExtended;
  46. procedure TestAsBoolean;
  47. procedure TestAsVariant;
  48. // Property tests - special values
  49. procedure TestIsNull;
  50. procedure TestIsUndefined;
  51. // Integration tests
  52. procedure TestIterateSimpleObject;
  53. procedure TestIterateSimpleArray;
  54. procedure TestIterateNestedStructure;
  55. procedure TestIterateMixedTypes;
  56. end;
  57. implementation
  58. { TJSONIteratorTest }
  59. function TJSONIteratorTest.CreateReaderFromJSON(const aJSON: string): TJsonTextReader;
  60. var
  61. StringReader: TStringReader;
  62. begin
  63. StringReader := TStringReader.Create(aJSON);
  64. Result := TJsonTextReader.Create(StringReader);
  65. Result.CloseInput := True;
  66. end;
  67. procedure TJSONIteratorTest.RewindCallback(aReader: TJsonReader);
  68. begin
  69. FRewindCalled := True;
  70. aReader.Rewind;
  71. end;
  72. function TJSONIteratorTest.IterateCallback(aIter: TJSONIterator): Boolean;
  73. begin
  74. Inc(FIterateCount);
  75. Result := True; // Continue iteration
  76. end;
  77. procedure TJSONIteratorTest.TestCreateWithReader;
  78. var
  79. Reader: TJsonTextReader;
  80. Iterator: TJSONIterator;
  81. begin
  82. Reader := CreateReaderFromJSON('{"test": 1}');
  83. try
  84. Iterator := TJSONIterator.Create(Reader);
  85. try
  86. CheckNotNull(Iterator, 'Iterator should be created');
  87. CheckEquals(Ord(TJsonToken.None), Ord(Iterator.&Type), 'Initial type should be None');
  88. finally
  89. Iterator.Free;
  90. end;
  91. finally
  92. Reader.Free;
  93. end;
  94. end;
  95. procedure TJSONIteratorTest.TestCreateWithReaderAndRewindProc;
  96. var
  97. Reader: TJsonTextReader;
  98. Iterator: TJSONIterator;
  99. begin
  100. FRewindCalled := False;
  101. Reader := CreateReaderFromJSON('{"test": 1}');
  102. try
  103. Iterator := TJSONIterator.Create(Reader, @RewindCallback);
  104. try
  105. CheckNotNull(Iterator, 'Iterator should be created');
  106. Iterator.Next;
  107. Iterator.Rewind;
  108. CheckTrue(FRewindCalled, 'Rewind callback should have been called');
  109. finally
  110. Iterator.Free;
  111. end;
  112. finally
  113. Reader.Free;
  114. end;
  115. end;
  116. procedure TJSONIteratorTest.TestNext;
  117. var
  118. Reader: TJsonTextReader;
  119. Iterator: TJSONIterator;
  120. HasNext: Boolean;
  121. begin
  122. Reader := CreateReaderFromJSON('{"name": "test", "value": 42}');
  123. try
  124. Iterator := TJSONIterator.Create(Reader);
  125. try
  126. HasNext := Iterator.Next;
  127. CheckTrue(HasNext, 'First Next should return True');
  128. HasNext := Iterator.Next;
  129. CheckTrue(HasNext, 'Second Next should return True');
  130. CheckEquals('name', Iterator.Key, 'Key should be "name"');
  131. CheckEquals('test', Iterator.asString, 'Value should be "test"');
  132. HasNext := Iterator.Next;
  133. CheckTrue(HasNext, 'Third Next should return True');
  134. CheckEquals('value', Iterator.Key, 'Key should be "value"');
  135. CheckEquals(42, Iterator.asInteger, 'Value should be 42');
  136. finally
  137. Iterator.Free;
  138. end;
  139. finally
  140. Reader.Free;
  141. end;
  142. end;
  143. procedure TJSONIteratorTest.TestNextWithKey;
  144. var
  145. Reader: TJsonTextReader;
  146. Iterator: TJSONIterator;
  147. Found: Boolean;
  148. begin
  149. Reader := CreateReaderFromJSON('{"first": 1, "second": 2, "third": 3}');
  150. try
  151. Iterator := TJSONIterator.Create(Reader);
  152. try
  153. // Skip to start
  154. Iterator.Next;
  155. // Find second key
  156. Found := False;
  157. while Iterator.Next do
  158. begin
  159. if Iterator.Key = 'second' then
  160. begin
  161. Found := True;
  162. CheckEquals(2, Iterator.asInteger, 'Value of "second" should be 2');
  163. Break;
  164. end;
  165. end;
  166. CheckTrue(Found, 'Should find key "second"');
  167. finally
  168. Iterator.Free;
  169. end;
  170. finally
  171. Reader.Free;
  172. end;
  173. end;
  174. procedure TJSONIteratorTest.TestRewind;
  175. var
  176. Reader: TJsonTextReader;
  177. Iterator: TJSONIterator;
  178. FirstKey: string;
  179. begin
  180. Reader := CreateReaderFromJSON('{"name": "test"}');
  181. try
  182. Iterator := TJSONIterator.Create(Reader, @RewindCallback);
  183. try
  184. Iterator.Next; // Start object
  185. Iterator.Next; // name: test
  186. FirstKey := Iterator.Key;
  187. Iterator.Rewind;
  188. Iterator.Next; // Start object again
  189. Iterator.Next; // name: test again
  190. CheckEquals(FirstKey, Iterator.Key, 'After rewind, should get same key');
  191. finally
  192. Iterator.Free;
  193. end;
  194. finally
  195. Reader.Free;
  196. end;
  197. end;
  198. procedure TJSONIteratorTest.TestRecurse;
  199. var
  200. Reader: TJsonTextReader;
  201. Iterator: TJSONIterator;
  202. CanRecurse: Boolean;
  203. begin
  204. Reader := CreateReaderFromJSON('{"nested": {"inner": 1}}');
  205. try
  206. Iterator := TJSONIterator.Create(Reader);
  207. try
  208. Iterator.Next; // Start object
  209. CheckEquals(Ord(TJsonToken.StartObject), Ord(Iterator.&Type), 'Should be at StartObject');
  210. CanRecurse := Iterator.Recurse;
  211. CheckTrue(CanRecurse, 'Should be able to recurse into object');
  212. CheckTrue(Iterator.InRecurse, 'InRecurse should be True after Recurse');
  213. finally
  214. Iterator.Free;
  215. end;
  216. finally
  217. Reader.Free;
  218. end;
  219. end;
  220. procedure TJSONIteratorTest.TestReturn;
  221. var
  222. Reader: TJsonTextReader;
  223. Iterator: TJSONIterator;
  224. InitialDepth: Integer;
  225. begin
  226. Reader := CreateReaderFromJSON('{"outer": {"inner": {"deep": 1}}, "after": 2}');
  227. try
  228. Iterator := TJSONIterator.Create(Reader);
  229. try
  230. Iterator.Next; // Start outer object
  231. InitialDepth := Iterator.Depth;
  232. // Go deeper
  233. Iterator.Next; // outer
  234. Iterator.Next; // inner object start
  235. Iterator.Next; // deep: 1
  236. CheckTrue(Iterator.Depth > InitialDepth, 'Should be deeper than initial');
  237. // Return to outer level
  238. Iterator.Return;
  239. // Should be able to continue to "after"
  240. if Iterator.Next then
  241. begin
  242. CheckEquals('after', Iterator.Key, 'Should reach "after" key');
  243. CheckEquals(2, Iterator.asInteger, 'Value of "after" should be 2');
  244. end;
  245. finally
  246. Iterator.Free;
  247. end;
  248. finally
  249. Reader.Free;
  250. end;
  251. end;
  252. procedure TJSONIteratorTest.TestFind;
  253. var
  254. Reader: TJsonTextReader;
  255. Iterator: TJSONIterator;
  256. Found: Boolean;
  257. begin
  258. Reader := CreateReaderFromJSON('{"name": "John", "age": 30}');
  259. try
  260. Iterator := TJSONIterator.Create(Reader, @RewindCallback);
  261. try
  262. Found := Iterator.Find('age');
  263. CheckTrue(Found, 'Should find "age" key');
  264. CheckEquals(30, Iterator.asInteger, 'Age value should be 30');
  265. finally
  266. Iterator.Free;
  267. end;
  268. finally
  269. Reader.Free;
  270. end;
  271. end;
  272. procedure TJSONIteratorTest.TestFindNestedPath;
  273. var
  274. Reader: TJsonTextReader;
  275. Iterator: TJSONIterator;
  276. Found: Boolean;
  277. begin
  278. Reader := CreateReaderFromJSON('{"person": {"name": "John", "address": {"city": "NYC"}}}');
  279. try
  280. Iterator := TJSONIterator.Create(Reader, @RewindCallback);
  281. try
  282. Found := Iterator.Find('person.address.city');
  283. CheckTrue(Found, 'Should find nested path "person.address.city"');
  284. CheckEquals('NYC', Iterator.asString, 'City value should be "NYC"');
  285. finally
  286. Iterator.Free;
  287. end;
  288. finally
  289. Reader.Free;
  290. end;
  291. end;
  292. procedure TJSONIteratorTest.TestIterate;
  293. var
  294. Reader: TJsonTextReader;
  295. Iterator: TJSONIterator;
  296. begin
  297. FIterateCount := 0;
  298. Reader := CreateReaderFromJSON('{"a": 1, "b": 2, "c": 3}');
  299. try
  300. Iterator := TJSONIterator.Create(Reader, @RewindCallback);
  301. try
  302. Iterator.Iterate(@IterateCallback);
  303. // Object start + 3 key-value pairs = 4 items (but we get only values after keys)
  304. CheckTrue(FIterateCount >= 3, 'Should iterate at least 3 times for 3 properties');
  305. finally
  306. Iterator.Free;
  307. end;
  308. finally
  309. Reader.Free;
  310. end;
  311. end;
  312. procedure TJSONIteratorTest.TestGetPathFromDepth;
  313. var
  314. Reader: TJsonTextReader;
  315. Iterator: TJSONIterator;
  316. PathStr: string;
  317. begin
  318. Reader := CreateReaderFromJSON('{"outer": {"inner": 1}}');
  319. try
  320. Iterator := TJSONIterator.Create(Reader);
  321. try
  322. Iterator.Next; // Start object
  323. Iterator.Next; // outer: { inner }
  324. Iterator.Next; // inner: 1
  325. PathStr := Iterator.GetPath(0);
  326. CheckTrue(Length(PathStr) > 0, 'Path should not be empty');
  327. finally
  328. Iterator.Free;
  329. end;
  330. finally
  331. Reader.Free;
  332. end;
  333. end;
  334. procedure TJSONIteratorTest.TestReaderProperty;
  335. var
  336. Reader: TJsonTextReader;
  337. Iterator: TJSONIterator;
  338. begin
  339. Reader := CreateReaderFromJSON('{"test": 1}');
  340. try
  341. Iterator := TJSONIterator.Create(Reader);
  342. try
  343. CheckTrue(Iterator.Reader = Reader, 'Reader property should return the same reader');
  344. finally
  345. Iterator.Free;
  346. end;
  347. finally
  348. Reader.Free;
  349. end;
  350. end;
  351. procedure TJSONIteratorTest.TestKeyProperty;
  352. var
  353. Reader: TJsonTextReader;
  354. Iterator: TJSONIterator;
  355. begin
  356. Reader := CreateReaderFromJSON('{"myKey": "myValue"}');
  357. try
  358. Iterator := TJSONIterator.Create(Reader);
  359. try
  360. CheckEquals('', Iterator.Key, 'Initial key should be empty');
  361. Iterator.Next; // Start object
  362. Iterator.Next; // myKey: myValue
  363. CheckEquals('myKey', Iterator.Key, 'Key should be "myKey"');
  364. finally
  365. Iterator.Free;
  366. end;
  367. finally
  368. Reader.Free;
  369. end;
  370. end;
  371. procedure TJSONIteratorTest.TestPathProperty;
  372. var
  373. Reader: TJsonTextReader;
  374. Iterator: TJSONIterator;
  375. begin
  376. Reader := CreateReaderFromJSON('{"level1": {"level2": 1}}');
  377. try
  378. Iterator := TJSONIterator.Create(Reader);
  379. try
  380. Iterator.Next; // Start object
  381. Iterator.Next; // level1
  382. Iterator.Next; // level2
  383. // Path property should return something meaningful
  384. CheckTrue(Length(Iterator.Path) >= 0, 'Path property should be accessible');
  385. finally
  386. Iterator.Free;
  387. end;
  388. finally
  389. Reader.Free;
  390. end;
  391. end;
  392. procedure TJSONIteratorTest.TestTypeProperty;
  393. var
  394. Reader: TJsonTextReader;
  395. Iterator: TJSONIterator;
  396. begin
  397. Reader := CreateReaderFromJSON('{"str": "hello", "num": 42, "bool": true}');
  398. try
  399. Iterator := TJSONIterator.Create(Reader);
  400. try
  401. Iterator.Next; // Start object
  402. CheckEquals(Ord(TJsonToken.StartObject), Ord(Iterator.&Type), 'Type should be StartObject');
  403. Iterator.Next; // str: hello
  404. CheckEquals(Ord(TJsonToken.&String), Ord(Iterator.&Type), 'Type should be String');
  405. Iterator.Next; // num: 42
  406. CheckEquals(Ord(TJsonToken.Integer), Ord(Iterator.&Type), 'Type should be Integer');
  407. Iterator.Next; // bool: true
  408. CheckEquals(Ord(TJsonToken.Boolean), Ord(Iterator.&Type), 'Type should be Boolean');
  409. finally
  410. Iterator.Free;
  411. end;
  412. finally
  413. Reader.Free;
  414. end;
  415. end;
  416. procedure TJSONIteratorTest.TestParentTypeProperty;
  417. var
  418. Reader: TJsonTextReader;
  419. Iterator: TJSONIterator;
  420. begin
  421. Reader := CreateReaderFromJSON('{"items": [1, 2, 3]}');
  422. try
  423. Iterator := TJSONIterator.Create(Reader);
  424. try
  425. Iterator.Next; // Start object
  426. CheckEquals(Ord(TJsonToken.StartObject), Ord(Iterator.ParentType), 'Parent should be StartObject');
  427. Iterator.Next; // items: [...]
  428. // After reading array start, parent should still be object
  429. CheckTrue(Iterator.ParentType in [TJsonToken.StartObject, TJsonToken.StartArray],
  430. 'Parent should be object or array');
  431. finally
  432. Iterator.Free;
  433. end;
  434. finally
  435. Reader.Free;
  436. end;
  437. end;
  438. procedure TJSONIteratorTest.TestIndexProperty;
  439. var
  440. Reader: TJsonTextReader;
  441. Iterator: TJSONIterator;
  442. begin
  443. Reader := CreateReaderFromJSON('[1, 2, 3]');
  444. try
  445. Iterator := TJSONIterator.Create(Reader);
  446. try
  447. CheckEquals(-1, Iterator.Index, 'Initial index should be -1');
  448. Iterator.Next; // Start array
  449. // Index starts at 0 for array contexts
  450. CheckTrue(Iterator.Index >= 0, 'Index should be >= 0 inside array');
  451. finally
  452. Iterator.Free;
  453. end;
  454. finally
  455. Reader.Free;
  456. end;
  457. end;
  458. procedure TJSONIteratorTest.TestInRecurseProperty;
  459. var
  460. Reader: TJsonTextReader;
  461. Iterator: TJSONIterator;
  462. begin
  463. Reader := CreateReaderFromJSON('{"nested": {}}');
  464. try
  465. Iterator := TJSONIterator.Create(Reader);
  466. try
  467. CheckFalse(Iterator.InRecurse, 'InRecurse should be False initially');
  468. Iterator.Next; // Start object
  469. Iterator.Recurse;
  470. CheckTrue(Iterator.InRecurse, 'InRecurse should be True after Recurse');
  471. finally
  472. Iterator.Free;
  473. end;
  474. finally
  475. Reader.Free;
  476. end;
  477. end;
  478. procedure TJSONIteratorTest.TestDepthProperty;
  479. var
  480. Reader: TJsonTextReader;
  481. Iterator: TJSONIterator;
  482. InitialDepth: Integer;
  483. begin
  484. Reader := CreateReaderFromJSON('{"level1": {"level2": {"level3": 1}}}');
  485. try
  486. Iterator := TJSONIterator.Create(Reader);
  487. try
  488. InitialDepth := Iterator.Depth;
  489. CheckEquals(0, InitialDepth, 'Initial depth should be 0');
  490. Iterator.Next; // Start object - depth 1
  491. CheckTrue(Iterator.Depth > InitialDepth, 'Depth should increase');
  492. Iterator.Next; // level1
  493. Iterator.Next; // level2
  494. CheckTrue(Iterator.Depth >= 2, 'Depth should be at least 2');
  495. finally
  496. Iterator.Free;
  497. end;
  498. finally
  499. Reader.Free;
  500. end;
  501. end;
  502. procedure TJSONIteratorTest.TestAsString;
  503. var
  504. Reader: TJsonTextReader;
  505. Iterator: TJSONIterator;
  506. begin
  507. Reader := CreateReaderFromJSON('{"message": "Hello, World!"}');
  508. try
  509. Iterator := TJSONIterator.Create(Reader);
  510. try
  511. Iterator.Next; // Start object
  512. Iterator.Next; // message: "Hello, World!"
  513. CheckEquals('Hello, World!', Iterator.asString, 'asString should return the string value');
  514. finally
  515. Iterator.Free;
  516. end;
  517. finally
  518. Reader.Free;
  519. end;
  520. end;
  521. procedure TJSONIteratorTest.TestAsInteger;
  522. var
  523. Reader: TJsonTextReader;
  524. Iterator: TJSONIterator;
  525. begin
  526. Reader := CreateReaderFromJSON('{"count": 12345}');
  527. try
  528. Iterator := TJSONIterator.Create(Reader);
  529. try
  530. Iterator.Next; // Start object
  531. Iterator.Next; // count: 12345
  532. CheckEquals(12345, Iterator.asInteger, 'asInteger should return 12345');
  533. finally
  534. Iterator.Free;
  535. end;
  536. finally
  537. Reader.Free;
  538. end;
  539. end;
  540. procedure TJSONIteratorTest.TestAsInt64;
  541. var
  542. Reader: TJsonTextReader;
  543. Iterator: TJSONIterator;
  544. begin
  545. Reader := CreateReaderFromJSON('{"bignum": 9223372036854775807}');
  546. try
  547. Iterator := TJSONIterator.Create(Reader);
  548. try
  549. Iterator.Next; // Start object
  550. Iterator.Next; // bignum
  551. CheckEquals(9223372036854775807, Iterator.asInt64, 'asInt64 should return max Int64');
  552. finally
  553. Iterator.Free;
  554. end;
  555. finally
  556. Reader.Free;
  557. end;
  558. end;
  559. procedure TJSONIteratorTest.TestAsDouble;
  560. var
  561. Reader: TJsonTextReader;
  562. Iterator: TJSONIterator;
  563. begin
  564. Reader := CreateReaderFromJSON('{"pi": 3.14159}');
  565. try
  566. Iterator := TJSONIterator.Create(Reader);
  567. try
  568. Iterator.Next; // Start object
  569. Iterator.Next; // pi: 3.14159
  570. CheckTrue(Abs(Iterator.asDouble - 3.14159) < 0.0001, 'asDouble should return approximately 3.14159');
  571. finally
  572. Iterator.Free;
  573. end;
  574. finally
  575. Reader.Free;
  576. end;
  577. end;
  578. procedure TJSONIteratorTest.TestAsExtended;
  579. var
  580. Reader: TJsonTextReader;
  581. Iterator: TJSONIterator;
  582. begin
  583. Reader := CreateReaderFromJSON('{"e": 2.718281828}');
  584. try
  585. Iterator := TJSONIterator.Create(Reader);
  586. try
  587. Iterator.Next; // Start object
  588. Iterator.Next; // e: 2.718281828
  589. CheckTrue(Abs(Iterator.asExtended - 2.718281828) < 0.0000001, 'asExtended should return approximately e');
  590. finally
  591. Iterator.Free;
  592. end;
  593. finally
  594. Reader.Free;
  595. end;
  596. end;
  597. procedure TJSONIteratorTest.TestAsBoolean;
  598. var
  599. Reader: TJsonTextReader;
  600. Iterator: TJSONIterator;
  601. begin
  602. Reader := CreateReaderFromJSON('{"active": true, "deleted": false}');
  603. try
  604. Iterator := TJSONIterator.Create(Reader);
  605. try
  606. Iterator.Next; // Start object
  607. Iterator.Next; // active: true
  608. CheckTrue(Iterator.asBoolean, 'asBoolean should return True');
  609. Iterator.Next; // deleted: false
  610. CheckFalse(Iterator.asBoolean, 'asBoolean should return False');
  611. finally
  612. Iterator.Free;
  613. end;
  614. finally
  615. Reader.Free;
  616. end;
  617. end;
  618. procedure TJSONIteratorTest.TestAsVariant;
  619. var
  620. Reader: TJsonTextReader;
  621. Iterator: TJSONIterator;
  622. V: Variant;
  623. begin
  624. Reader := CreateReaderFromJSON('{"value": "test variant"}');
  625. try
  626. Iterator := TJSONIterator.Create(Reader);
  627. try
  628. Iterator.Next; // Start object
  629. Iterator.Next; // value: "test variant"
  630. V := Iterator.asVariant;
  631. CheckEquals('test variant', string(V), 'asVariant should return correct value');
  632. finally
  633. Iterator.Free;
  634. end;
  635. finally
  636. Reader.Free;
  637. end;
  638. end;
  639. procedure TJSONIteratorTest.TestIsNull;
  640. var
  641. Reader: TJsonTextReader;
  642. Iterator: TJSONIterator;
  643. begin
  644. Reader := CreateReaderFromJSON('{"empty": null, "notempty": 1}');
  645. try
  646. Iterator := TJSONIterator.Create(Reader);
  647. try
  648. Iterator.Next; // Start object
  649. Iterator.Next; // empty: null
  650. CheckTrue(Iterator.IsNull, 'IsNull should return True for null value');
  651. Iterator.Next; // notempty: 1
  652. CheckFalse(Iterator.IsNull, 'IsNull should return False for non-null value');
  653. finally
  654. Iterator.Free;
  655. end;
  656. finally
  657. Reader.Free;
  658. end;
  659. end;
  660. procedure TJSONIteratorTest.TestIsUndefined;
  661. var
  662. Reader: TJsonTextReader;
  663. Iterator: TJSONIterator;
  664. begin
  665. // Note: undefined is not standard JSON but may be supported
  666. Reader := CreateReaderFromJSON('{"value": 1}');
  667. try
  668. Iterator := TJSONIterator.Create(Reader);
  669. try
  670. Iterator.Next; // Start object
  671. Iterator.Next; // value: 1
  672. CheckFalse(Iterator.IsUndefined, 'IsUndefined should return False for regular value');
  673. finally
  674. Iterator.Free;
  675. end;
  676. finally
  677. Reader.Free;
  678. end;
  679. end;
  680. procedure TJSONIteratorTest.TestIterateSimpleObject;
  681. var
  682. Reader: TJsonTextReader;
  683. Iterator: TJSONIterator;
  684. Keys: string;
  685. begin
  686. Reader := CreateReaderFromJSON('{"a": 1, "b": 2}');
  687. try
  688. Iterator := TJSONIterator.Create(Reader);
  689. try
  690. Keys := '';
  691. while Iterator.Next do
  692. begin
  693. if Iterator.Key <> '' then
  694. Keys := Keys + Iterator.Key + ',';
  695. end;
  696. CheckEquals('a,b,', Keys, 'Should iterate through all keys');
  697. finally
  698. Iterator.Free;
  699. end;
  700. finally
  701. Reader.Free;
  702. end;
  703. end;
  704. procedure TJSONIteratorTest.TestIterateSimpleArray;
  705. var
  706. Reader: TJsonTextReader;
  707. Iterator: TJSONIterator;
  708. Sum: Integer;
  709. begin
  710. Reader := CreateReaderFromJSON('[1, 2, 3, 4, 5]');
  711. try
  712. Iterator := TJSONIterator.Create(Reader);
  713. try
  714. Sum := 0;
  715. while Iterator.Next do
  716. begin
  717. if Iterator.&Type = TJsonToken.Integer then
  718. Sum := Sum + Iterator.asInteger;
  719. end;
  720. CheckEquals(15, Sum, 'Sum of array elements should be 15');
  721. finally
  722. Iterator.Free;
  723. end;
  724. finally
  725. Reader.Free;
  726. end;
  727. end;
  728. procedure TJSONIteratorTest.TestIterateNestedStructure;
  729. var
  730. Reader: TJsonTextReader;
  731. Iterator: TJSONIterator;
  732. FoundDeep: Boolean;
  733. begin
  734. Reader := CreateReaderFromJSON('{"level1": {"level2": {"level3": "deep value"}}}');
  735. try
  736. Iterator := TJSONIterator.Create(Reader);
  737. try
  738. FoundDeep := False;
  739. while Iterator.Next do
  740. begin
  741. if (Iterator.Key = 'level3') and (Iterator.asString = 'deep value') then
  742. begin
  743. FoundDeep := True;
  744. Break;
  745. end;
  746. end;
  747. CheckTrue(FoundDeep, 'Should find deeply nested value');
  748. finally
  749. Iterator.Free;
  750. end;
  751. finally
  752. Reader.Free;
  753. end;
  754. end;
  755. procedure TJSONIteratorTest.TestIterateMixedTypes;
  756. var
  757. Reader: TJsonTextReader;
  758. Iterator: TJSONIterator;
  759. TypeCount: Integer;
  760. begin
  761. Reader := CreateReaderFromJSON('{"str": "hello", "num": 42, "float": 3.14, "bool": true, "nil": null, "arr": [1,2], "obj": {"a":1}}');
  762. try
  763. Iterator := TJSONIterator.Create(Reader);
  764. try
  765. TypeCount := 0;
  766. while Iterator.Next do
  767. begin
  768. case Iterator.&Type of
  769. TJsonToken.&String,
  770. TJsonToken.Integer,
  771. TJsonToken.Float,
  772. TJsonToken.Boolean,
  773. TJsonToken.Null,
  774. TJsonToken.StartArray,
  775. TJsonToken.StartObject:
  776. Inc(TypeCount);
  777. end;
  778. end;
  779. CheckTrue(TypeCount >= 7, 'Should encounter at least 7 different value types');
  780. finally
  781. Iterator.Free;
  782. end;
  783. finally
  784. Reader.Free;
  785. end;
  786. end;
  787. initialization
  788. RegisterTest(TJSONIteratorTest.Suite);
  789. end.