utcjsonreaders.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. unit utcjsonreaders;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, fpcunit, testutils, testregistry,
  6. System.JSON.Readers, system.json, StreamEx;
  7. type
  8. { TTestTJsonTextReader }
  9. TTestTJsonTextReader = class(TTestCase)
  10. private
  11. FReader: TJsonTextReader;
  12. FStringReader: TStringReader;
  13. procedure SetupTextReader(const AJsonText: string);
  14. procedure TeardownTextReader;
  15. protected
  16. procedure SetUp; override;
  17. procedure TearDown; override;
  18. published
  19. procedure TestCreate;
  20. procedure TestReadSimpleObject;
  21. procedure TestReadSimpleArray;
  22. procedure TestReadMixedTypes;
  23. procedure TestReadNestedObjects;
  24. procedure TestReadNestedArrays;
  25. procedure TestReadAsInteger;
  26. procedure TestReadAsInt64;
  27. procedure TestReadAsUInt64;
  28. procedure TestReadAsString;
  29. procedure TestReadAsDouble;
  30. procedure TestReadAsDateTime;
  31. procedure TestReadAsBytes;
  32. procedure TestSkip;
  33. procedure TestClose;
  34. procedure TestRewind;
  35. procedure TestGetLineNumber;
  36. procedure TestGetLinePosition;
  37. procedure TestHasLineInfo;
  38. procedure TestProperties;
  39. procedure TestErrorHandling;
  40. procedure TestEmptyInput;
  41. procedure TestNullValues;
  42. procedure TestBooleanValues;
  43. procedure TestNumberFormats;
  44. procedure TestStringEscaping;
  45. end;
  46. { TTestTJsonObjectReader }
  47. TTestTJsonObjectReader = class(TTestCase)
  48. private
  49. FReader: TJsonObjectReader;
  50. FJsonObject: TJSONObject;
  51. protected
  52. procedure SetUp; override;
  53. procedure TearDown; override;
  54. published
  55. procedure TestCreateWithObject;
  56. procedure TestCreateWithArray;
  57. procedure TestReadFromSimpleObject;
  58. procedure TestReadFromSimpleArray;
  59. procedure TestReadFromNestedStructure;
  60. procedure TestRewindAndReread;
  61. procedure TestGetCurrent;
  62. procedure TestCloseAndReset;
  63. end;
  64. { TTestEJsonReaderException }
  65. TTestEJsonReaderException = class(TTestCase)
  66. published
  67. procedure TestCreateWithReader;
  68. procedure TestCreateWithPath;
  69. procedure TestCreateFmt;
  70. procedure TestProperties;
  71. end;
  72. implementation
  73. uses
  74. TypInfo, Math;
  75. { TTestTJsonTextReader }
  76. procedure TTestTJsonTextReader.SetupTextReader(const AJsonText: string);
  77. begin
  78. TeardownTextReader;
  79. FStringReader := TStringReader.Create(AJsonText);
  80. FReader := TJsonTextReader.Create(FStringReader);
  81. end;
  82. procedure TTestTJsonTextReader.TeardownTextReader;
  83. begin
  84. FreeAndNil(FReader);
  85. FreeAndNil(FStringReader);
  86. end;
  87. procedure TTestTJsonTextReader.SetUp;
  88. begin
  89. inherited SetUp;
  90. FReader := nil;
  91. FStringReader := nil;
  92. end;
  93. procedure TTestTJsonTextReader.TearDown;
  94. begin
  95. TeardownTextReader;
  96. inherited TearDown;
  97. end;
  98. procedure TTestTJsonTextReader.TestCreate;
  99. var
  100. StringReader: TStringReader;
  101. Reader: TJsonTextReader;
  102. begin
  103. StringReader := TStringReader.Create('{}');
  104. try
  105. Reader := TJsonTextReader.Create(StringReader);
  106. try
  107. AssertNotNull('Reader should be created', Reader);
  108. AssertEquals('Initial state should be Start', Ord(TState.Start), Ord(Reader.CurrentState));
  109. finally
  110. Reader.Free;
  111. end;
  112. finally
  113. StringReader.Free;
  114. end;
  115. end;
  116. procedure TTestTJsonTextReader.TestReadSimpleObject;
  117. begin
  118. SetupTextReader('{"name": "John", "age": 30}');
  119. // Start Object
  120. AssertTrue('Should read start object', FReader.Read);
  121. AssertEquals('Should be StartObject token', Ord(TJsonToken.StartObject), Ord(FReader.TokenType));
  122. // Property name
  123. AssertTrue('Should read property name', FReader.Read);
  124. AssertEquals('Should be PropertyName token', Ord(TJsonToken.PropertyName), Ord(FReader.TokenType));
  125. AssertEquals('Property name should be "name"', 'name', FReader.Value.AsString);
  126. // Property value
  127. AssertTrue('Should read property value', FReader.Read);
  128. AssertEquals('Should be String token', Ord(TJsonToken.&String), Ord(FReader.TokenType));
  129. AssertEquals('Property value should be "John"', 'John', FReader.Value.AsString);
  130. // Second property name
  131. AssertTrue('Should read second property name', FReader.Read);
  132. AssertEquals('Should be PropertyName token', Ord(TJsonToken.PropertyName), Ord(FReader.TokenType));
  133. AssertEquals('Property name should be "age"', 'age', FReader.Value.AsString);
  134. // Second property value
  135. AssertTrue('Should read second property value', FReader.Read);
  136. AssertEquals('Should be Integer token', Ord(TJsonToken.Integer), Ord(FReader.TokenType));
  137. AssertEquals('Property value should be 30', 30, FReader.Value.AsInteger);
  138. // End Object
  139. AssertTrue('Should read end object', FReader.Read);
  140. AssertEquals('Should be EndObject token', Ord(TJsonToken.EndObject), Ord(FReader.TokenType));
  141. // Should be done
  142. AssertFalse('Should be done reading', FReader.Read);
  143. end;
  144. procedure TTestTJsonTextReader.TestReadSimpleArray;
  145. begin
  146. SetupTextReader('[1, 2, 3]');
  147. // Start Array
  148. AssertTrue('Should read start array', FReader.Read);
  149. AssertEquals('Should be StartArray token', Ord(TJsonToken.StartArray), Ord(FReader.TokenType));
  150. // First element
  151. AssertTrue('Should read first element', FReader.Read);
  152. AssertEquals('Should be Integer token', Ord(TJsonToken.Integer), Ord(FReader.TokenType));
  153. AssertEquals('Value should be 1', 1, FReader.Value.AsInteger);
  154. // Second element
  155. AssertTrue('Should read second element', FReader.Read);
  156. AssertEquals('Should be Integer token', Ord(TJsonToken.Integer), Ord(FReader.TokenType));
  157. AssertEquals('Value should be 2', 2, FReader.Value.AsInteger);
  158. // Third element
  159. AssertTrue('Should read third element', FReader.Read);
  160. AssertEquals('Should be Integer token', Ord(TJsonToken.Integer), Ord(FReader.TokenType));
  161. AssertEquals('Value should be 3', 3, FReader.Value.AsInteger);
  162. // End Array
  163. AssertTrue('Should read end array', FReader.Read);
  164. AssertEquals('Should be EndArray token', Ord(TJsonToken.EndArray), Ord(FReader.TokenType));
  165. // Should be done
  166. AssertFalse('Should be done reading', FReader.Read);
  167. end;
  168. procedure TTestTJsonTextReader.TestReadMixedTypes;
  169. begin
  170. SetupTextReader('{"string": "hello", "number": 42, "float": 3.14, "bool": true, "null": null}');
  171. AssertTrue('Should read start object', FReader.Read);
  172. AssertEquals('Should be StartObject', Ord(TJsonToken.StartObject), Ord(FReader.TokenType));
  173. // String property
  174. AssertTrue('Should read string property name', FReader.Read);
  175. AssertEquals('Property name', 'string', FReader.Value.AsString);
  176. AssertTrue('Should read string value', FReader.Read);
  177. AssertEquals('Should be String token', Ord(TJsonToken.&String), Ord(FReader.TokenType));
  178. AssertEquals('String value', 'hello', FReader.Value.AsString);
  179. // Number property
  180. AssertTrue('Should read number property name', FReader.Read);
  181. AssertEquals('Property name', 'number', FReader.Value.AsString);
  182. AssertTrue('Should read number value', FReader.Read);
  183. AssertEquals('Should be Integer token', Ord(TJsonToken.Integer), Ord(FReader.TokenType));
  184. // Float property
  185. AssertTrue('Should read float property name', FReader.Read);
  186. AssertEquals('Property name', 'float', FReader.Value.AsString);
  187. AssertTrue('Should read float value', FReader.Read);
  188. AssertEquals('Should be Float token', Ord(TJsonToken.Float), Ord(FReader.TokenType));
  189. // Boolean property
  190. AssertTrue('Should read bool property name', FReader.Read);
  191. AssertEquals('Property name', 'bool', FReader.Value.AsString);
  192. AssertTrue('Should read bool value', FReader.Read);
  193. AssertEquals('Should be Boolean token', Ord(TJsonToken.Boolean), Ord(FReader.TokenType));
  194. // Null property
  195. AssertTrue('Should read null property name', FReader.Read);
  196. AssertEquals('Property name', 'null', FReader.Value.AsString);
  197. AssertTrue('Should read null value', FReader.Read);
  198. AssertEquals('Should be Null token', Ord(TJsonToken.Null), Ord(FReader.TokenType));
  199. AssertTrue('Should read end object', FReader.Read);
  200. AssertEquals('Should be EndObject', Ord(TJsonToken.EndObject), Ord(FReader.TokenType));
  201. end;
  202. procedure TTestTJsonTextReader.TestReadNestedObjects;
  203. begin
  204. SetupTextReader('{"outer": {"inner": "value"}}');
  205. AssertTrue('Should read start object', FReader.Read);
  206. AssertTrue('Should read outer property', FReader.Read);
  207. AssertEquals('Property name', 'outer', FReader.Value.AsString);
  208. AssertTrue('Should read inner start object', FReader.Read);
  209. AssertEquals('Should be StartObject', Ord(TJsonToken.StartObject), Ord(FReader.TokenType));
  210. AssertTrue('Should read inner property', FReader.Read);
  211. AssertEquals('Property name', 'inner', FReader.Value.AsString);
  212. AssertTrue('Should read inner value', FReader.Read);
  213. AssertEquals('Property value', 'value', FReader.Value.AsString);
  214. AssertTrue('Should read inner end object', FReader.Read);
  215. AssertEquals('Should be EndObject', Ord(TJsonToken.EndObject), Ord(FReader.TokenType));
  216. AssertTrue('Should read outer end object', FReader.Read);
  217. AssertEquals('Should be EndObject', Ord(TJsonToken.EndObject), Ord(FReader.TokenType));
  218. end;
  219. procedure TTestTJsonTextReader.TestReadNestedArrays;
  220. begin
  221. SetupTextReader('[[1, 2], [3, 4]]');
  222. AssertTrue('Should read start array', FReader.Read);
  223. AssertTrue('Should read inner start array', FReader.Read);
  224. AssertTrue('Should read first element', FReader.Read);
  225. AssertTrue('Should read second element', FReader.Read);
  226. AssertTrue('Should read inner end array', FReader.Read);
  227. AssertTrue('Should read second inner start array', FReader.Read);
  228. AssertTrue('Should read third element', FReader.Read);
  229. AssertTrue('Should read fourth element', FReader.Read);
  230. AssertTrue('Should read second inner end array', FReader.Read);
  231. AssertTrue('Should read outer end array', FReader.Read);
  232. end;
  233. procedure TTestTJsonTextReader.TestReadAsInteger;
  234. begin
  235. SetupTextReader('{"number": 42}');
  236. FReader.Read; // start object
  237. FReader.Read; // property name
  238. AssertEquals('Should read as integer', 42, FReader.ReadAsInteger);
  239. end;
  240. procedure TTestTJsonTextReader.TestReadAsInt64;
  241. begin
  242. SetupTextReader('{"number": 9223372036854775807}');
  243. FReader.Read; // start object
  244. FReader.Read; // property name
  245. AssertEquals('Should read as Int64', Int64(9223372036854775807), FReader.ReadAsInt64);
  246. end;
  247. procedure TTestTJsonTextReader.TestReadAsUInt64;
  248. begin
  249. SetupTextReader('{"number": 18446744073709551615}');
  250. FReader.Read; // start object
  251. FReader.Read; // property name
  252. // Note: This is a very large number, so we'll test with a smaller one
  253. SetupTextReader('{"number": 123}');
  254. FReader.Read; // start object
  255. FReader.Read; // property name
  256. AssertEquals('Should read as UInt64', UInt64(123), FReader.ReadAsUInt64);
  257. end;
  258. procedure TTestTJsonTextReader.TestReadAsString;
  259. begin
  260. SetupTextReader('{"text": "hello world"}');
  261. FReader.Read; // start object
  262. FReader.Read; // property name
  263. AssertEquals('Should read as string', 'hello world', FReader.ReadAsString);
  264. end;
  265. procedure TTestTJsonTextReader.TestReadAsDouble;
  266. begin
  267. SetupTextReader('{"number": 3.14159}');
  268. FReader.Read; // start object
  269. FReader.Read; // property name
  270. AssertEquals('Should read as double', 3.14159, FReader.ReadAsDouble, 0.00001);
  271. end;
  272. procedure TTestTJsonTextReader.TestReadAsDateTime;
  273. begin
  274. SetupTextReader('{"date": "2023-01-01"}');
  275. FReader.Read; // start object
  276. FReader.Read; // property name
  277. // This will test date parsing - may return 0 if parsing fails
  278. FReader.ReadAsDateTime;
  279. AssertTrue('DateTime reading should not crash', True);
  280. end;
  281. procedure TTestTJsonTextReader.TestReadAsBytes;
  282. begin
  283. SetupTextReader('{"data": "SGVsbG8="}');
  284. FReader.Read; // start object
  285. FReader.Read; // property name
  286. // This tests Base64 decoding
  287. FReader.ReadAsBytes;
  288. AssertTrue('Bytes reading should not crash', True);
  289. end;
  290. procedure TTestTJsonTextReader.TestSkip;
  291. begin
  292. SetupTextReader('{"skip": {"nested": "value"}, "keep": "data"}');
  293. FReader.Read; // start object
  294. FReader.Read; // "skip" property name
  295. FReader.Skip; // Skip the nested object
  296. AssertTrue('Should read after skip', FReader.Read);
  297. AssertEquals('Should be "keep" property', 'keep', FReader.Value.AsString);
  298. end;
  299. procedure TTestTJsonTextReader.TestClose;
  300. begin
  301. SetupTextReader('{}');
  302. FReader.Close;
  303. AssertEquals('State should be Closed', Ord(TState.Closed), Ord(FReader.CurrentState));
  304. end;
  305. procedure TTestTJsonTextReader.TestRewind;
  306. begin
  307. SetupTextReader('{"test": "value"}');
  308. FReader.Read; // start object
  309. FReader.Read; // property name
  310. FReader.Rewind;
  311. AssertEquals('State should be Start after rewind', Ord(TState.Start), Ord(FReader.CurrentState));
  312. // Should be able to read from beginning again
  313. AssertTrue('Should read start object after rewind', FReader.Read);
  314. AssertEquals('Should be StartObject after rewind', Ord(TJsonToken.StartObject), Ord(FReader.TokenType));
  315. end;
  316. procedure TTestTJsonTextReader.TestGetLineNumber;
  317. begin
  318. SetupTextReader('{}');
  319. // Line number should be available
  320. AssertTrue('Line number should be >= 1', FReader.GetLineNumber >= 1);
  321. end;
  322. procedure TTestTJsonTextReader.TestGetLinePosition;
  323. begin
  324. SetupTextReader('{}');
  325. // Line position should be available
  326. AssertTrue('Line position should be >= 0', FReader.GetLinePosition >= 0);
  327. end;
  328. procedure TTestTJsonTextReader.TestHasLineInfo;
  329. begin
  330. SetupTextReader('{}');
  331. AssertTrue('Should have line info', FReader.HasLineInfo);
  332. end;
  333. procedure TTestTJsonTextReader.TestProperties;
  334. begin
  335. SetupTextReader('{}');
  336. // Test property accessors
  337. AssertEquals('Initial depth should be 0', 0, FReader.Depth);
  338. AssertEquals('Default MaxDepth should be 64', 64, FReader.MaxDepth);
  339. AssertEquals('Default QuoteChar should be "', '"', FReader.QuoteChar);
  340. AssertFalse('Default SupportMultipleContent should be false', FReader.SupportMultipleContent);
  341. AssertFalse('Default CloseInput should be false', FReader.CloseInput);
  342. // Test property setters
  343. FReader.MaxDepth := 32;
  344. AssertEquals('MaxDepth should be settable', 32, FReader.MaxDepth);
  345. FReader.QuoteChar := '''';
  346. AssertEquals('QuoteChar should be settable', '''', FReader.QuoteChar);
  347. FReader.SupportMultipleContent := True;
  348. AssertTrue('SupportMultipleContent should be settable', FReader.SupportMultipleContent);
  349. FReader.CloseInput := True;
  350. AssertTrue('CloseInput should be settable', FReader.CloseInput);
  351. end;
  352. procedure TTestTJsonTextReader.TestErrorHandling;
  353. var
  354. ExceptionRaised: Boolean;
  355. begin
  356. SetupTextReader('{"invalid": }');
  357. ExceptionRaised := False;
  358. try
  359. FReader.Read; // start object
  360. FReader.Read; // property name
  361. FReader.Read; // should fail on invalid syntax
  362. except
  363. on EJsonReaderException do
  364. ExceptionRaised := True;
  365. end;
  366. AssertTrue('Should raise exception on invalid JSON', ExceptionRaised);
  367. end;
  368. procedure TTestTJsonTextReader.TestEmptyInput;
  369. begin
  370. SetupTextReader('');
  371. AssertFalse('Should return false for empty input', FReader.Read);
  372. end;
  373. procedure TTestTJsonTextReader.TestNullValues;
  374. begin
  375. SetupTextReader('{"nullValue": null}');
  376. FReader.Read; // start object
  377. FReader.Read; // property name
  378. FReader.Read; // null value
  379. AssertEquals('Should be Null token', Ord(TJsonToken.Null), Ord(FReader.TokenType));
  380. end;
  381. procedure TTestTJsonTextReader.TestBooleanValues;
  382. begin
  383. SetupTextReader('{"trueValue": true, "falseValue": false}');
  384. FReader.Read; // start object
  385. FReader.Read; // "trueValue" property name
  386. FReader.Read; // true value
  387. AssertEquals('Should be Boolean token', Ord(TJsonToken.Boolean), Ord(FReader.TokenType));
  388. FReader.Read; // "falseValue" property name
  389. FReader.Read; // false value
  390. AssertEquals('Should be Boolean token', Ord(TJsonToken.Boolean), Ord(FReader.TokenType));
  391. end;
  392. procedure TTestTJsonTextReader.TestNumberFormats;
  393. begin
  394. SetupTextReader('{"int": 42, "float": 3.14, "exp": 1.23e-4}');
  395. FReader.Read; // start object
  396. // Integer
  397. FReader.Read; // property name
  398. FReader.Read; // integer value
  399. AssertEquals('Should be Integer token', Ord(TJsonToken.Integer), Ord(FReader.TokenType));
  400. // Float
  401. FReader.Read; // property name
  402. FReader.Read; // float value
  403. AssertEquals('Should be Float token', Ord(TJsonToken.Float), Ord(FReader.TokenType));
  404. // Scientific notation
  405. FReader.Read; // property name
  406. FReader.Read; // exponential value
  407. AssertEquals('Should be Float token for exponential', Ord(TJsonToken.Float), Ord(FReader.TokenType));
  408. end;
  409. procedure TTestTJsonTextReader.TestStringEscaping;
  410. begin
  411. SetupTextReader('{"escaped": "hello\\nworld\\t\"quote\""}');
  412. FReader.Read; // start object
  413. FReader.Read; // property name
  414. FReader.Read; // escaped string value
  415. AssertEquals('Should be String token', Ord(TJsonToken.&String), Ord(FReader.TokenType));
  416. // The actual escaped content depends on the scanner implementation
  417. AssertTrue('Should have some content', Length(FReader.Value.AsString) > 0);
  418. end;
  419. { TTestTJsonObjectReader }
  420. procedure TTestTJsonObjectReader.SetUp;
  421. begin
  422. inherited SetUp;
  423. FJsonObject := nil;
  424. FReader := nil;
  425. end;
  426. procedure TTestTJsonObjectReader.TearDown;
  427. begin
  428. FreeAndNil(FReader);
  429. FreeAndNil(FJsonObject);
  430. inherited TearDown;
  431. end;
  432. procedure TTestTJsonObjectReader.TestCreateWithObject;
  433. begin
  434. FJsonObject := TJSONObject.Create;
  435. FJsonObject.AddPair('name', 'John');
  436. FReader := TJsonObjectReader.Create(FJsonObject);
  437. AssertNotNull('Reader should be created', FReader);
  438. AssertSame('Current should be the object', FJsonObject, FReader.Current);
  439. end;
  440. procedure TTestTJsonObjectReader.TestCreateWithArray;
  441. var
  442. JsonArray: TJSONArray;
  443. begin
  444. JsonArray := TJSONArray.Create;
  445. JsonArray.Add('item1');
  446. JsonArray.Add('item2');
  447. try
  448. FReader := TJsonObjectReader.Create(JsonArray);
  449. AssertNotNull('Reader should be created', FReader);
  450. finally
  451. JsonArray.Free;
  452. end;
  453. end;
  454. procedure TTestTJsonObjectReader.TestReadFromSimpleObject;
  455. begin
  456. FJsonObject := TJSONObject.Create;
  457. FJsonObject.AddPair('name', 'John');
  458. FJsonObject.AddPair('age', 30);
  459. FReader := TJsonObjectReader.Create(FJsonObject);
  460. // Read start object
  461. AssertTrue('Should read start object', FReader.Read);
  462. AssertEquals('Should be StartObject', Ord(TJsonToken.StartObject), Ord(FReader.TokenType));
  463. // Read first property
  464. AssertTrue('Should read property name', FReader.Read);
  465. AssertEquals('Should be PropertyName', Ord(TJsonToken.PropertyName), Ord(FReader.TokenType));
  466. AssertTrue('Should read property value', FReader.Read);
  467. AssertEquals('Should be String', Ord(TJsonToken.&String), Ord(FReader.TokenType));
  468. // Read second property
  469. AssertTrue('Should read second property name', FReader.Read);
  470. AssertEquals('Should be PropertyName', Ord(TJsonToken.PropertyName), Ord(FReader.TokenType));
  471. AssertTrue('Should read second property value', FReader.Read);
  472. AssertEquals('Should be Integer', Ord(TJsonToken.Integer), Ord(FReader.TokenType));
  473. // Read end object
  474. AssertTrue('Should read end object', FReader.Read);
  475. AssertEquals('Should be EndObject', Ord(TJsonToken.EndObject), Ord(FReader.TokenType));
  476. end;
  477. procedure TTestTJsonObjectReader.TestReadFromSimpleArray;
  478. var
  479. JsonArray: TJSONArray;
  480. begin
  481. JsonArray := TJSONArray.Create;
  482. JsonArray.Add('item1');
  483. JsonArray.Add(42);
  484. JsonArray.Add(True);
  485. try
  486. FReader := TJsonObjectReader.Create(JsonArray);
  487. // Read start array
  488. AssertTrue('Should read start array', FReader.Read);
  489. AssertEquals('Should be StartArray', Ord(TJsonToken.StartArray), Ord(FReader.TokenType));
  490. // Read elements
  491. AssertTrue('Should read first element', FReader.Read);
  492. AssertEquals('Should be String', Ord(TJsonToken.&String), Ord(FReader.TokenType));
  493. AssertTrue('Should read second element', FReader.Read);
  494. AssertEquals('Should be Integer', Ord(TJsonToken.Integer), Ord(FReader.TokenType));
  495. AssertTrue('Should read third element', FReader.Read);
  496. AssertEquals('Should be Boolean', Ord(TJsonToken.Boolean), Ord(FReader.TokenType));
  497. // Read end array
  498. AssertTrue('Should read end array', FReader.Read);
  499. AssertEquals('Should be EndArray', Ord(TJsonToken.EndArray), Ord(FReader.TokenType));
  500. finally
  501. JsonArray.Free;
  502. end;
  503. end;
  504. procedure TTestTJsonObjectReader.TestReadFromNestedStructure;
  505. var
  506. InnerObject: TJSONObject;
  507. begin
  508. InnerObject := TJSONObject.Create;
  509. InnerObject.AddPair('inner', 'value');
  510. FJsonObject := TJSONObject.Create;
  511. FJsonObject.AddPair('outer', InnerObject);
  512. FReader := TJsonObjectReader.Create(FJsonObject);
  513. // Should be able to read the nested structure
  514. AssertTrue('Should read start object', FReader.Read);
  515. AssertTrue('Should read outer property', FReader.Read);
  516. AssertTrue('Should read inner start object', FReader.Read);
  517. AssertTrue('Should read inner property', FReader.Read);
  518. AssertTrue('Should read inner value', FReader.Read);
  519. AssertTrue('Should read inner end object', FReader.Read);
  520. AssertTrue('Should read outer end object', FReader.Read);
  521. end;
  522. procedure TTestTJsonObjectReader.TestRewindAndReread;
  523. begin
  524. FJsonObject := TJSONObject.Create;
  525. FJsonObject.AddPair('test', 'value');
  526. FReader := TJsonObjectReader.Create(FJsonObject);
  527. // Read once
  528. AssertTrue('First read should work', FReader.Read);
  529. AssertTrue('Should read property', FReader.Read);
  530. // Rewind
  531. FReader.Rewind;
  532. AssertEquals('State should be Start', Ord(TState.Start), Ord(FReader.CurrentState));
  533. // Read again
  534. AssertTrue('Should read start object after rewind', FReader.Read);
  535. AssertEquals('Should be StartObject after rewind', Ord(TJsonToken.StartObject), Ord(FReader.TokenType));
  536. end;
  537. procedure TTestTJsonObjectReader.TestGetCurrent;
  538. begin
  539. FJsonObject := TJSONObject.Create;
  540. FReader := TJsonObjectReader.Create(FJsonObject);
  541. AssertSame('Current should return the root object', FJsonObject, FReader.Current);
  542. end;
  543. procedure TTestTJsonObjectReader.TestCloseAndReset;
  544. begin
  545. FJsonObject := TJSONObject.Create;
  546. FJsonObject.AddPair('test', 'value');
  547. FReader := TJsonObjectReader.Create(FJsonObject);
  548. FReader.Close;
  549. AssertEquals('State should be Closed', Ord(TState.Closed), Ord(FReader.CurrentState));
  550. end;
  551. { TTestEJsonReaderException }
  552. procedure TTestEJsonReaderException.TestCreateWithReader;
  553. var
  554. Reader: TJsonTextReader;
  555. StringReader: TStringReader;
  556. Exception: EJsonReaderException;
  557. begin
  558. StringReader := TStringReader.Create('{}');
  559. Reader := TJsonTextReader.Create(StringReader);
  560. try
  561. Exception := EJsonReaderException.Create(Reader, 'Test message');
  562. try
  563. AssertEquals('Message should be set', 'Test message', Exception.Message);
  564. AssertEquals('Path should be from reader', Reader.Path, Exception.Path);
  565. AssertEquals('Line number should be from reader', Reader.GetLineNumber, Exception.LineNumber);
  566. AssertEquals('Line position should be from reader', Reader.GetLinePosition, Exception.LinePosition);
  567. finally
  568. Exception.Free;
  569. end;
  570. finally
  571. Reader.Free;
  572. StringReader.Free;
  573. end;
  574. end;
  575. procedure TTestEJsonReaderException.TestCreateWithPath;
  576. var
  577. Exception: EJsonReaderException;
  578. begin
  579. Exception := EJsonReaderException.Create('Test message', 'test.path', 10, 5);
  580. try
  581. AssertEquals('Message should be set', 'Test message', Exception.Message);
  582. AssertEquals('Path should be set', 'test.path', Exception.Path);
  583. AssertEquals('Line number should be set', 10, Exception.LineNumber);
  584. AssertEquals('Line position should be set', 5, Exception.LinePosition);
  585. finally
  586. Exception.Free;
  587. end;
  588. end;
  589. procedure TTestEJsonReaderException.TestCreateFmt;
  590. var
  591. Reader: TJsonTextReader;
  592. StringReader: TStringReader;
  593. Exception: EJsonReaderException;
  594. begin
  595. StringReader := TStringReader.Create('{}');
  596. Reader := TJsonTextReader.Create(StringReader);
  597. try
  598. Exception := EJsonReaderException.CreateFmt(Reader, 'Test %s %d', ['message', 42]);
  599. try
  600. AssertEquals('Message should be formatted', 'Test message 42', Exception.Message);
  601. finally
  602. Exception.Free;
  603. end;
  604. finally
  605. Reader.Free;
  606. StringReader.Free;
  607. end;
  608. end;
  609. procedure TTestEJsonReaderException.TestProperties;
  610. var
  611. Exception: EJsonReaderException;
  612. begin
  613. Exception := EJsonReaderException.Create('Test', 'path', 1, 2);
  614. try
  615. AssertEquals('Path property', 'path', Exception.Path);
  616. AssertEquals('LineNumber property', 1, Exception.LineNumber);
  617. AssertEquals('LinePosition property', 2, Exception.LinePosition);
  618. finally
  619. Exception.Free;
  620. end;
  621. end;
  622. initialization
  623. RegisterTest(TTestTJsonTextReader);
  624. RegisterTest(TTestTJsonObjectReader);
  625. RegisterTest(TTestEJsonReaderException);
  626. end.