jsonreader.pp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. {
  2. This file is part of the Free Component Library
  3. JSON SAX-like Reader
  4. Copyright (c) 2007 by Michael Van Canneyt [email protected]
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit jsonreader;
  12. {$I fcl-json.inc}
  13. interface
  14. uses
  15. Classes, SysUtils, fpJSON, jsonscanner;
  16. Type
  17. { TBaseJSONReader }
  18. TBaseJSONReader = Class(TObject)
  19. Private
  20. FScanner : TJSONScanner;
  21. function GetO(AIndex: TJSONOption): Boolean;
  22. function GetOptions: TJSONOptions; inline;
  23. procedure SetO(AIndex: TJSONOption; AValue: Boolean);
  24. procedure SetOptions(AValue: TJSONOptions);
  25. Protected
  26. procedure DoError(const Msg: String);
  27. Procedure DoParse(AtCurrent,AllowEOF: Boolean);
  28. function GetNextToken: TJSONToken;
  29. function CurrentTokenString: RawByteString;
  30. function CurrentToken: TJSONToken; inline;
  31. Procedure KeyValue(Const AKey : TJSONStringType); virtual; abstract;
  32. Procedure StringValue(Const AValue : TJSONStringType);virtual; abstract;
  33. Procedure NullValue; virtual; abstract;
  34. Procedure FloatValue(Const AValue : Double); virtual; abstract;
  35. Procedure BooleanValue(Const AValue : Boolean); virtual; abstract;
  36. Procedure NumberValue(Const AValue : TJSONStringType); virtual; abstract;
  37. Procedure IntegerValue(Const AValue : integer); virtual; abstract;
  38. Procedure Int64Value(Const AValue : int64); virtual; abstract;
  39. Procedure QWordValue(Const AValue : QWord); virtual; abstract;
  40. Procedure StartArray; virtual; abstract;
  41. Procedure StartObject; virtual; abstract;
  42. Procedure EndArray; virtual; abstract;
  43. Procedure EndObject; virtual; abstract;
  44. Procedure ParseArray;
  45. Procedure ParseObject;
  46. Procedure ParseNumber;
  47. Procedure DoExecute;
  48. Property Scanner : TJSONScanner read FScanner;
  49. Public
  50. Constructor Create(Source : TStream; AUseUTF8 : Boolean = True); overload;deprecated 'use options form instead';
  51. Constructor Create(Const Source : RawByteString; AUseUTF8 : Boolean = True); overload;deprecated 'use options form instead';
  52. constructor Create(Source: TStream; AOptions: TJSONOptions); overload;
  53. constructor Create(const Source: RawByteString; AOptions: TJSONOptions); overload;
  54. destructor Destroy();override;
  55. // Parsing options
  56. Property Options : TJSONOptions Read GetOptions Write SetOptions;
  57. end;
  58. TOnJSONBoolean = Procedure (Sender : TObject; Const AValue : Boolean) of object;
  59. TOnJSONFloat = Procedure (Sender : TObject; Const AValue : TJSONFloat) of object;
  60. TOnJSONInt64 = Procedure (Sender : TObject; Const AValue : Int64) of object;
  61. TOnJSONQWord = Procedure (Sender : TObject; Const AValue : QWord) of object;
  62. TOnJSONInteger = Procedure (Sender : TObject; Const AValue : Integer) of object;
  63. TOnJSONString = Procedure (Sender : TObject; Const AValue : TJSONStringType) of Object;
  64. TOnJSONKey = Procedure (Sender : TObject; Const AKey : TJSONStringType) of Object;
  65. { TJSONEventReader }
  66. TJSONEventReader = Class(TBaseJSONReader)
  67. Private
  68. FOnBooleanValue: TOnJSONBoolean;
  69. FOnEndArray: TNotifyEvent;
  70. FOnEndObject: TNotifyEvent;
  71. FOnFloatValue: TOnJSONFloat;
  72. FOnInt64Value: TOnJSONInt64;
  73. FOnIntegerValue: TOnJSONInteger;
  74. FOnKeyName: TOnJSONKey;
  75. FOnNullValue: TNotifyEvent;
  76. FOnNumberValue: TOnJSONString;
  77. FOnQWordValue: TOnJSONQWord;
  78. FOnStartArray: TNotifyEvent;
  79. FOnStartObject: TNotifyEvent;
  80. FOnStringValue: TOnJSONString;
  81. Protected
  82. Procedure KeyValue(Const AKey : TJSONStringType); override;
  83. Procedure StringValue(Const AValue : TJSONStringType);override;
  84. Procedure NullValue; override;
  85. Procedure FloatValue(Const AValue : Double); override;
  86. Procedure BooleanValue(Const AValue : Boolean); override;
  87. Procedure NumberValue(Const AValue : TJSONStringType); override;
  88. Procedure IntegerValue(Const AValue : integer); override;
  89. Procedure Int64Value(Const AValue : int64); override;
  90. Procedure QWordValue(Const AValue : QWord); override;
  91. Procedure StartArray; override;
  92. Procedure StartObject; override;
  93. Procedure EndArray; override;
  94. Procedure EndObject; override;
  95. Public
  96. Procedure Execute;
  97. Property OnNullValue : TNotifyEvent Read FOnNullValue Write FOnNullValue;
  98. Property OnBooleanValue : TOnJSONBoolean Read FOnBooleanValue Write FOnBooleanValue;
  99. Property OnNumberValue : TOnJSONString Read FOnNumberValue Write FOnNumberValue;
  100. Property OnFloatValue : TOnJSONFloat Read FOnFloatValue Write FOnFloatValue;
  101. Property OnIntegerValue : TOnJSONInteger Read FOnIntegerValue Write FOnIntegerValue;
  102. Property OnInt64Value : TOnJSONInt64 Read FOnInt64Value Write FOnInt64Value;
  103. Property OnQWordValue : TOnJSONQWord Read FOnQWordValue Write FOnQWordValue;
  104. Property OnStringValue : TOnJSONString Read FOnStringValue Write FOnStringValue;
  105. Property OnKeyName : TOnJSONKey Read FOnKeyName Write FOnKeyName;
  106. Property OnStartObject : TNotifyEvent Read FOnStartObject Write FOnStartObject;
  107. Property OnEndObject : TNotifyEvent Read FOnEndObject Write FOnEndObject;
  108. Property OnStartArray : TNotifyEvent Read FOnStartArray Write FOnStartArray;
  109. Property OnEndArray : TNotifyEvent Read FOnEndArray Write FOnEndArray;
  110. end;
  111. IJSONConsumer = Interface ['{60F9D640-2A69-4AAB-8EE1-0DB6DC614D27}']
  112. Procedure NullValue;
  113. Procedure BooleanValue (const AValue : Boolean);
  114. Procedure NumberValue (const AValue : TJSONStringType);
  115. Procedure FloatValue (const AValue : TJSONFloat);
  116. Procedure Int64Value (const AValue : Int64);
  117. Procedure QWordValue (const AValue : QWord);
  118. Procedure IntegerValue(const AValue : Integer) ;
  119. Procedure StringValue(const AValue : TJSONStringType) ;
  120. Procedure KeyName(const AKey : TJSONStringType);
  121. Procedure StartObject;
  122. Procedure EndObject;
  123. Procedure StartArray;
  124. Procedure EndArray;
  125. end;
  126. { TJSONConsumerReader }
  127. TJSONConsumerReader = Class(TBaseJSONReader)
  128. Private
  129. FConsumer: IJSONConsumer;
  130. Protected
  131. Procedure KeyValue(Const AKey : TJSONStringType); override;
  132. Procedure StringValue(Const AValue : TJSONStringType);override;
  133. Procedure NullValue; override;
  134. Procedure FloatValue(Const AValue : Double); override;
  135. Procedure BooleanValue(Const AValue : Boolean); override;
  136. Procedure NumberValue(Const AValue : TJSONStringType); override;
  137. Procedure IntegerValue(Const AValue : integer); override;
  138. Procedure Int64Value(Const AValue : int64); override;
  139. Procedure QWordValue(Const AValue : QWord); override;
  140. Procedure StartArray; override;
  141. Procedure StartObject; override;
  142. Procedure EndArray; override;
  143. Procedure EndObject; override;
  144. Public
  145. Procedure Execute;
  146. Property Consumer : IJSONConsumer Read FConsumer Write FConsumer;
  147. end;
  148. EJSONParser = Class(EParserError);
  149. implementation
  150. Resourcestring
  151. SErrUnexpectedEOF = 'Unexpected EOF encountered.';
  152. SErrUnexpectedToken = 'Unexpected token (%s) encountered.';
  153. SErrExpectedColon = 'Expected colon (:), got token "%s".';
  154. //SErrEmptyElement = 'Empty element encountered.';
  155. SErrExpectedElementName = 'Expected element name, got token "%s"';
  156. SExpectedCommaorBraceClose = 'Expected comma (,) or square bracket (]), got token "%s".';
  157. SErrInvalidNumber = 'Number is not an integer or real number: %s';
  158. SErrNoScanner = 'No scanner. No source specified ?';
  159. SErrorAt = 'Error at line %d, Pos %d: ';
  160. SErrGarbageFound = 'Expected EOF, but got %s';
  161. { TBaseJSONReader }
  162. Procedure TBaseJSONReader.DoExecute;
  163. begin
  164. if (FScanner=Nil) then
  165. DoError(SErrNoScanner);
  166. DoParse(False,True);
  167. if joStrict in Options then
  168. begin
  169. Repeat
  170. GetNextToken;
  171. Until CurrentToken<>tkWhiteSpace;
  172. If CurrentToken<>tkEOF then
  173. DoError(Format(SErrGarbageFound,[CurrentTokenString]));
  174. end;
  175. end;
  176. {
  177. Consume next token and convert to JSON data structure.
  178. If AtCurrent is true, the current token is used. If false,
  179. a token is gotten from the scanner.
  180. If AllowEOF is false, encountering a tkEOF will result in an exception.
  181. }
  182. function TBaseJSONReader.CurrentToken: TJSONToken;
  183. begin
  184. Result:=FScanner.CurToken;
  185. end;
  186. function TBaseJSONReader.CurrentTokenString: RawByteString;
  187. begin
  188. If CurrentToken in [tkString,tkIdentifier,tkNumber,tkComment] then
  189. Result:=FScanner.CurTokenString
  190. else
  191. Result:=TokenInfos[CurrentToken];
  192. end;
  193. procedure TBaseJSONReader.DoParse(AtCurrent, AllowEOF: Boolean);
  194. var
  195. T : TJSONToken;
  196. begin
  197. If not AtCurrent then
  198. T:=GetNextToken
  199. else
  200. T:=FScanner.CurToken;
  201. Case T of
  202. tkEof : If Not AllowEof then
  203. DoError(SErrUnexpectedEOF);
  204. tkNull : NullValue;
  205. tkTrue,
  206. tkFalse : BooleanValue(t=tkTrue);
  207. tkString : if (joUTF8 in Options) and (DefaultSystemCodePage<>CP_UTF8) then
  208. StringValue(TJSONStringType(UTF8Decode(CurrentTokenString)))
  209. else
  210. StringValue(CurrentTokenString);
  211. tkCurlyBraceOpen :
  212. ParseObject;
  213. tkCurlyBraceClose :
  214. DoError(SErrUnexpectedToken);
  215. tkSQuaredBraceOpen :
  216. ParseArray;
  217. tkSQuaredBraceClose :
  218. DoError(SErrUnexpectedToken);
  219. tkNumber :
  220. ParseNumber;
  221. tkComma :
  222. DoError(SErrUnexpectedToken);
  223. tkIdentifier :
  224. DoError(SErrUnexpectedToken);
  225. else
  226. // Do nothing
  227. end;
  228. end;
  229. // Creates the correct JSON number type, based on the current token.
  230. procedure TBaseJSONReader.ParseNumber;
  231. Var
  232. I : Integer;
  233. I64 : Int64;
  234. QW : QWord;
  235. F : TJSONFloat;
  236. S : String;
  237. begin
  238. S:=CurrentTokenString;
  239. NumberValue(S);
  240. I:=0;
  241. if TryStrToQWord(S,QW) then
  242. begin
  243. if QW>qword(high(Int64)) then
  244. QWordValue(QW)
  245. else
  246. if QW>MaxInt then
  247. begin
  248. I64 := QW;
  249. Int64Value(I64);
  250. end
  251. else
  252. begin
  253. I:=QW;
  254. IntegerValue(I);
  255. end
  256. end
  257. else
  258. begin
  259. If TryStrToInt64(S,I64) then
  260. if (I64>Maxint) or (I64<-MaxInt) then
  261. Int64Value(I64)
  262. Else
  263. begin
  264. I:=I64;
  265. IntegerValue(I);
  266. end
  267. else
  268. begin
  269. I:=0;
  270. Val(S,F,I);
  271. If (I<>0) then
  272. DoError(SErrInvalidNumber);
  273. FloatValue(F);
  274. end;
  275. end;
  276. end;
  277. function TBaseJSONReader.GetO(AIndex: TJSONOption): Boolean;
  278. begin
  279. Result:=AIndex in Options;
  280. end;
  281. function TBaseJSONReader.GetOptions: TJSONOptions;
  282. begin
  283. Result:=FScanner.Options
  284. end;
  285. procedure TBaseJSONReader.SetO(AIndex: TJSONOption; AValue: Boolean);
  286. begin
  287. if aValue then
  288. FScanner.Options:=FScanner.Options+[AINdex]
  289. else
  290. FScanner.Options:=FScanner.Options-[AINdex]
  291. end;
  292. procedure TBaseJSONReader.SetOptions(AValue: TJSONOptions);
  293. begin
  294. FScanner.Options:=AValue;
  295. end;
  296. // Current token is {, on exit current token is }
  297. Procedure TBaseJSONReader.ParseObject;
  298. Var
  299. T : TJSONtoken;
  300. LastComma : Boolean;
  301. S : TJSONStringType;
  302. begin
  303. LastComma:=False;
  304. StartObject;
  305. T:=GetNextToken;
  306. While T<>tkCurlyBraceClose do
  307. begin
  308. If (T<>tkString) and (T<>tkIdentifier) then
  309. DoError(SErrExpectedElementName);
  310. S:=CurrentTokenString;
  311. KeyValue(S);
  312. // Writeln(S);
  313. T:=GetNextToken;
  314. If (T<>tkColon) then
  315. DoError(SErrExpectedColon);
  316. DoParse(False,False);
  317. T:=GetNextToken;
  318. If Not (T in [tkComma,tkCurlyBraceClose]) then
  319. DoError(SExpectedCommaorBraceClose);
  320. If T=tkComma then
  321. begin
  322. T:=GetNextToken;
  323. LastComma:=(t=tkCurlyBraceClose);
  324. end;
  325. end;
  326. If LastComma and ((joStrict in Options) or not (joIgnoreTrailingComma in Options)) then // Test for ,} case
  327. DoError(SErrUnExpectedToken);
  328. EndObject;
  329. end;
  330. // Current token is [, on exit current token is ]
  331. Procedure TBaseJSONReader.ParseArray;
  332. Var
  333. T : TJSONtoken;
  334. LastComma : Boolean;
  335. S : TJSONOPTions;
  336. begin
  337. StartArray;
  338. LastComma:=False;
  339. Repeat
  340. T:=GetNextToken;
  341. If (T<>tkSquaredBraceClose) then
  342. begin
  343. DoParse(True,False);
  344. T:=GetNextToken;
  345. If Not (T in [tkComma,tkSquaredBraceClose]) then
  346. DoError(SExpectedCommaorBraceClose);
  347. LastComma:=(t=TkComma);
  348. end;
  349. Until (T=tkSquaredBraceClose);
  350. S:=Options;
  351. If LastComma and ((joStrict in S) or not (joIgnoreTrailingComma in S)) then // Test for ,] case
  352. DoError(SErrUnExpectedToken);
  353. EndArray;
  354. end;
  355. // Get next token, discarding whitespace
  356. function TBaseJSONReader.GetNextToken: TJSONToken;
  357. begin
  358. Repeat
  359. Result:=FScanner.FetchToken;
  360. Until (Not (Result in [tkComment,tkWhiteSpace]));
  361. end;
  362. procedure TBaseJSONReader.DoError(const Msg: String);
  363. Var
  364. S : String;
  365. begin
  366. S:=Format(Msg,[CurrentTokenString]);
  367. S:=Format(SErrorAt,[FScanner.CurRow,FSCanner.CurColumn])+S;
  368. Raise EJSONParser.Create(S);
  369. end;
  370. constructor TBaseJSONReader.Create(Source: TStream; AUseUTF8 : Boolean = True);
  371. begin
  372. Inherited Create;
  373. FScanner:=TJSONScanner.Create(Source,[joUTF8]);
  374. if AUseUTF8 then
  375. Options:=Options + [joUTF8];
  376. end;
  377. constructor TBaseJSONReader.Create(const Source: RawByteString; AUseUTF8 : Boolean = True);
  378. begin
  379. Inherited Create;
  380. FScanner:=TJSONScanner.Create(Source,[joUTF8]);
  381. if AUseUTF8 then
  382. Options:=Options + [joUTF8];
  383. end;
  384. constructor TBaseJSONReader.Create(Source: TStream; AOptions: TJSONOptions);
  385. begin
  386. FScanner:=TJSONScanner.Create(Source,AOptions);
  387. end;
  388. constructor TBaseJSONReader.Create(const Source: RawByteString; AOptions: TJSONOptions);
  389. begin
  390. FScanner:=TJSONScanner.Create(Source,AOptions);
  391. end;
  392. destructor TBaseJSONReader.Destroy();
  393. begin
  394. FreeAndNil(FScanner);
  395. inherited Destroy();
  396. end;
  397. { TJSONReader }
  398. procedure TJSONEventReader.KeyValue(const AKey: TJSONStringType);
  399. begin
  400. if Assigned(FOnKeyName) then
  401. FOnKeyName(Self,AKey);
  402. end;
  403. procedure TJSONEventReader.StringValue(const AValue: TJSONStringType);
  404. begin
  405. if Assigned(FOnStringValue) then
  406. FOnStringValue(Self,AValue);
  407. end;
  408. procedure TJSONEventReader.NullValue;
  409. begin
  410. if Assigned(FOnNullValue) then
  411. FOnNullValue(Self);
  412. end;
  413. procedure TJSONEventReader.FloatValue(const AValue: Double);
  414. begin
  415. if Assigned(FOnFloatValue) then
  416. FOnFloatValue(Self,AValue);
  417. end;
  418. procedure TJSONEventReader.BooleanValue(const AValue: Boolean);
  419. begin
  420. if Assigned(FOnBooleanValue) then
  421. FOnBooleanValue(Self,AValue);
  422. end;
  423. procedure TJSONEventReader.NumberValue(const AValue: TJSONStringType);
  424. begin
  425. if Assigned(FOnNumberValue) then
  426. FOnNumberValue(Self,AValue);
  427. end;
  428. procedure TJSONEventReader.IntegerValue(const AValue: integer);
  429. begin
  430. if Assigned(FOnIntegerValue) then
  431. FOnIntegerValue(Self,AValue);
  432. end;
  433. procedure TJSONEventReader.Int64Value(const AValue: int64);
  434. begin
  435. if Assigned(FOnInt64Value) then
  436. FOnInt64Value(Self,AValue);
  437. end;
  438. procedure TJSONEventReader.QWordValue(const AValue: QWord);
  439. begin
  440. if Assigned(FOnQWordValue) then
  441. FOnQWordValue(Self,AValue);
  442. end;
  443. procedure TJSONEventReader.StartArray;
  444. begin
  445. If Assigned(FOnStartArray) then
  446. FOnStartArray(Self);
  447. end;
  448. procedure TJSONEventReader.StartObject;
  449. begin
  450. if Assigned(FOnStartObject) then
  451. FOnStartObject(Self);
  452. end;
  453. procedure TJSONEventReader.EndArray;
  454. begin
  455. If Assigned(FOnEndArray) then
  456. FOnEndArray(Self);
  457. end;
  458. procedure TJSONEventReader.EndObject;
  459. begin
  460. If Assigned(FOnEndObject) then
  461. FOnEndObject(Self);
  462. end;
  463. procedure TJSONEventReader.Execute;
  464. begin
  465. DoExecute;
  466. end;
  467. { TJSONConsumerReader }
  468. procedure TJSONConsumerReader.KeyValue(const AKey: TJSONStringType);
  469. begin
  470. If Assigned(FConsumer) then
  471. FConsumer.KeyName(Akey)
  472. end;
  473. procedure TJSONConsumerReader.StringValue(const AValue: TJSONStringType);
  474. begin
  475. If Assigned(FConsumer) then
  476. FConsumer.StringValue(AValue);
  477. end;
  478. procedure TJSONConsumerReader.NullValue;
  479. begin
  480. If Assigned(FConsumer) then
  481. FConsumer.NullValue;
  482. end;
  483. procedure TJSONConsumerReader.FloatValue(const AValue: Double);
  484. begin
  485. If Assigned(FConsumer) then
  486. FConsumer.FloatValue(AValue);
  487. end;
  488. procedure TJSONConsumerReader.BooleanValue(const AValue: Boolean);
  489. begin
  490. If Assigned(FConsumer) then
  491. FConsumer.BooleanValue(AValue);
  492. end;
  493. procedure TJSONConsumerReader.NumberValue(const AValue: TJSONStringType);
  494. begin
  495. If Assigned(FConsumer) then
  496. FConsumer.NumberValue(AValue);
  497. end;
  498. procedure TJSONConsumerReader.IntegerValue(const AValue: integer);
  499. begin
  500. If Assigned(FConsumer) then
  501. FConsumer.IntegerValue(AValue);
  502. end;
  503. procedure TJSONConsumerReader.Int64Value(const AValue: int64);
  504. begin
  505. If Assigned(FConsumer) then
  506. FConsumer.Int64Value(AValue);
  507. end;
  508. procedure TJSONConsumerReader.QWordValue(const AValue: QWord);
  509. begin
  510. If Assigned(FConsumer) then
  511. FConsumer.QWordValue(AValue);
  512. end;
  513. procedure TJSONConsumerReader.StartArray;
  514. begin
  515. if Assigned(FConsumer) then
  516. FConsumer.StartArray;
  517. end;
  518. procedure TJSONConsumerReader.StartObject;
  519. begin
  520. if Assigned(FConsumer) then
  521. FConsumer.StartObject;
  522. end;
  523. procedure TJSONConsumerReader.EndArray;
  524. begin
  525. if Assigned(FConsumer) then
  526. FConsumer.EndArray;
  527. end;
  528. procedure TJSONConsumerReader.EndObject;
  529. begin
  530. if Assigned(FConsumer) then
  531. FConsumer.EndObject;
  532. end;
  533. procedure TJSONConsumerReader.Execute;
  534. begin
  535. DoExecute;
  536. end;
  537. end.