jsonparser.pp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. {
  2. This file is part of the Free Component Library
  3. JSON source parser
  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. {$mode objfpc}
  12. {$h+}
  13. unit jsonparser;
  14. interface
  15. uses
  16. Classes, SysUtils, fpJSON, jsonscanner;
  17. Type
  18. { TJSONParser }
  19. TJSONParser = Class(TObject)
  20. Private
  21. FScanner : TJSONScanner;
  22. function GetO(AIndex: TJSONOption): Boolean;
  23. function GetOptions: TJSONOptions; inline;
  24. function ParseNumber: TJSONNumber;
  25. procedure SetO(AIndex: TJSONOption; AValue: Boolean);
  26. procedure SetOptions(AValue: TJSONOptions);
  27. Protected
  28. procedure DoError(const Msg: String);
  29. function DoParse(AtCurrent,AllowEOF: Boolean): TJSONData;
  30. function GetNextToken: TJSONToken;
  31. function CurrentTokenString: String;
  32. function CurrentToken: TJSONToken; inline;
  33. function ParseArray: TJSONArray;
  34. function ParseObject: TJSONObject;
  35. Property Scanner : TJSONScanner read FScanner;
  36. Public
  37. function Parse: TJSONData;
  38. Constructor Create(Source : TStream; AUseUTF8 : Boolean = True); overload;deprecated 'use options form instead';
  39. Constructor Create(Source : TJSONStringType; AUseUTF8 : Boolean = True); overload;deprecated 'use options form instead';
  40. constructor Create(Source: TStream; AOptions: TJSONOptions); overload;
  41. constructor Create(const Source: String; AOptions: TJSONOptions); overload;
  42. destructor Destroy();override;
  43. // Use strict JSON: " for strings, object members are strings, not identifiers
  44. Property Strict : Boolean Index joStrict Read GetO Write SetO ; deprecated 'use options instead';
  45. // if set to TRUE, then strings will be converted to UTF8 ansistrings, not system codepage ansistrings.
  46. Property UseUTF8 : Boolean index joUTF8 Read GetO Write SetO; deprecated 'Use options instead';
  47. // Parsing options
  48. Property Options : TJSONOptions Read GetOptions Write SetOptions;
  49. end;
  50. EJSONParser = Class(EParserError);
  51. implementation
  52. Resourcestring
  53. SErrUnexpectedEOF = 'Unexpected EOF encountered.';
  54. SErrUnexpectedToken = 'Unexpected token (%s) encountered.';
  55. SErrExpectedColon = 'Expected colon (:), got token "%s".';
  56. SErrEmptyElement = 'Empty element encountered.';
  57. SErrExpectedElementName = 'Expected element name, got token "%s"';
  58. SExpectedCommaorBraceClose = 'Expected , or ], got token "%s".';
  59. SErrInvalidNumber = 'Number is not an integer or real number: %s';
  60. SErrNoScanner = 'No scanner. No source specified ?';
  61. { TJSONParser }
  62. procedure DefJSONParserHandler(AStream: TStream; const AUseUTF8: Boolean; out
  63. Data: TJSONData);
  64. Var
  65. P : TJSONParser;
  66. begin
  67. Data:=Nil;
  68. P:=TJSONParser.Create(AStream,[joUTF8]);
  69. try
  70. Data:=P.Parse;
  71. finally
  72. P.Free;
  73. end;
  74. end;
  75. function TJSONParser.Parse: TJSONData;
  76. begin
  77. if (FScanner=Nil) then
  78. DoError(SErrNoScanner);
  79. Result:=DoParse(False,True);
  80. end;
  81. {
  82. Consume next token and convert to JSON data structure.
  83. If AtCurrent is true, the current token is used. If false,
  84. a token is gotten from the scanner.
  85. If AllowEOF is false, encountering a tkEOF will result in an exception.
  86. }
  87. function TJSONParser.CurrentToken: TJSONToken;
  88. begin
  89. Result:=FScanner.CurToken;
  90. end;
  91. function TJSONParser.CurrentTokenString: String;
  92. begin
  93. If CurrentToken in [tkString,tkIdentifier,tkNumber,tkComment] then
  94. Result:=FScanner.CurTokenString
  95. else
  96. Result:=TokenInfos[CurrentToken];
  97. end;
  98. function TJSONParser.DoParse(AtCurrent, AllowEOF: Boolean): TJSONData;
  99. var
  100. T : TJSONToken;
  101. begin
  102. Result:=nil;
  103. try
  104. If not AtCurrent then
  105. T:=GetNextToken
  106. else
  107. T:=FScanner.CurToken;
  108. Case T of
  109. tkEof : If Not AllowEof then
  110. DoError(SErrUnexpectedEOF);
  111. tkNull : Result:=CreateJSON;
  112. tkTrue,
  113. tkFalse : Result:=CreateJSON(t=tkTrue);
  114. tkString : if joUTF8 in Options then
  115. Result:=CreateJSON(UTF8Decode(CurrentTokenString))
  116. else
  117. Result:=CreateJSON(CurrentTokenString);
  118. tkCurlyBraceOpen : Result:=ParseObject;
  119. tkCurlyBraceClose : DoError(SErrUnexpectedToken);
  120. tkSQuaredBraceOpen : Result:=ParseArray;
  121. tkSQuaredBraceClose : DoError(SErrUnexpectedToken);
  122. tkNumber : Result:=ParseNumber;
  123. tkComma : DoError(SErrUnexpectedToken);
  124. tkIdentifier : DoError(SErrUnexpectedToken);
  125. end;
  126. except
  127. FreeAndNil(Result);
  128. Raise;
  129. end;
  130. end;
  131. // Creates the correct JSON number type, based on the current token.
  132. function TJSONParser.ParseNumber: TJSONNumber;
  133. Var
  134. I : Integer;
  135. I64 : Int64;
  136. QW : QWord;
  137. F : TJSONFloat;
  138. S : String;
  139. begin
  140. S:=CurrentTokenString;
  141. I:=0;
  142. if TryStrToQWord(S,QW) then
  143. begin
  144. if QW>qword(high(Int64)) then
  145. Result:=CreateJSON(QW)
  146. else
  147. if QW>MaxInt then
  148. begin
  149. I64 := QW;
  150. Result:=CreateJSON(I64);
  151. end
  152. else
  153. begin
  154. I := QW;
  155. Result:=CreateJSON(I);
  156. end
  157. end
  158. else
  159. begin
  160. If TryStrToInt64(S,I64) then
  161. if (I64>Maxint) or (I64<-MaxInt) then
  162. Result:=CreateJSON(I64)
  163. Else
  164. begin
  165. I:=I64;
  166. Result:=CreateJSON(I);
  167. end
  168. else
  169. begin
  170. I:=0;
  171. Val(S,F,I);
  172. If (I<>0) then
  173. DoError(SErrInvalidNumber);
  174. Result:=CreateJSON(F);
  175. end;
  176. end;
  177. end;
  178. function TJSONParser.GetO(AIndex: TJSONOption): Boolean;
  179. begin
  180. Result:=AIndex in Options;
  181. end;
  182. function TJSONParser.GetOptions: TJSONOptions;
  183. begin
  184. Result:=FScanner.Options
  185. end;
  186. procedure TJSONParser.SetO(AIndex: TJSONOption; AValue: Boolean);
  187. begin
  188. if aValue then
  189. FScanner.Options:=FScanner.Options+[AINdex]
  190. else
  191. FScanner.Options:=FScanner.Options-[AINdex]
  192. end;
  193. procedure TJSONParser.SetOptions(AValue: TJSONOptions);
  194. begin
  195. FScanner.Options:=AValue;
  196. end;
  197. // Current token is {, on exit current token is }
  198. function TJSONParser.ParseObject: TJSONObject;
  199. Var
  200. T : TJSONtoken;
  201. E : TJSONData;
  202. N : String;
  203. LastComma : Boolean;
  204. begin
  205. LastComma:=False;
  206. Result:=CreateJSONObject([]);
  207. Try
  208. T:=GetNextToken;
  209. While T<>tkCurlyBraceClose do
  210. begin
  211. If (T<>tkString) and (T<>tkIdentifier) then
  212. DoError(SErrExpectedElementName);
  213. N:=CurrentTokenString;
  214. T:=GetNextToken;
  215. If (T<>tkColon) then
  216. DoError(SErrExpectedColon);
  217. E:=DoParse(False,False);
  218. Result.Add(N,E);
  219. T:=GetNextToken;
  220. If Not (T in [tkComma,tkCurlyBraceClose]) then
  221. DoError(SExpectedCommaorBraceClose);
  222. If T=tkComma then
  223. begin
  224. T:=GetNextToken;
  225. LastComma:=(t=tkCurlyBraceClose);
  226. end;
  227. end;
  228. If LastComma and ((joStrict in Options) or not (joIgnoreTrailingComma in Options)) then // Test for ,} case
  229. DoError(SErrUnExpectedToken);
  230. Except
  231. FreeAndNil(Result);
  232. Raise;
  233. end;
  234. end;
  235. // Current token is [, on exit current token is ]
  236. function TJSONParser.ParseArray: TJSONArray;
  237. Var
  238. T : TJSONtoken;
  239. E : TJSONData;
  240. LastComma : Boolean;
  241. S : TJSONOPTions;
  242. begin
  243. Result:=CreateJSONArray([]);
  244. LastComma:=False;
  245. Try
  246. Repeat
  247. T:=GetNextToken;
  248. If (T<>tkSquaredBraceClose) then
  249. begin
  250. E:=DoParse(True,False);
  251. If (E<>Nil) then
  252. Result.Add(E)
  253. else if (Result.Count>0) then
  254. DoError(SErrEmptyElement);
  255. T:=GetNextToken;
  256. If Not (T in [tkComma,tkSquaredBraceClose]) then
  257. DoError(SExpectedCommaorBraceClose);
  258. LastComma:=(t=TkComma);
  259. end;
  260. Until (T=tkSquaredBraceClose);
  261. S:=Options;
  262. If LastComma and ((joStrict in S) or not (joIgnoreTrailingComma in S)) then // Test for ,] case
  263. DoError(SErrUnExpectedToken);
  264. Except
  265. FreeAndNil(Result);
  266. Raise;
  267. end;
  268. end;
  269. // Get next token, discarding whitespace
  270. function TJSONParser.GetNextToken: TJSONToken;
  271. begin
  272. Repeat
  273. Result:=FScanner.FetchToken;
  274. Until (Not (Result in [tkComment,tkWhiteSpace]));
  275. end;
  276. procedure TJSONParser.DoError(const Msg: String);
  277. Var
  278. S : String;
  279. begin
  280. S:=Format(Msg,[CurrentTokenString]);
  281. S:=Format('Error at line %d, Pos %d:',[FScanner.CurRow,FSCanner.CurColumn])+S;
  282. Raise EJSONParser.Create(S);
  283. end;
  284. constructor TJSONParser.Create(Source: TStream; AUseUTF8 : Boolean = True);
  285. begin
  286. Inherited Create;
  287. FScanner:=TJSONScanner.Create(Source,[joUTF8]);
  288. if AUseUTF8 then
  289. Options:=Options + [joUTF8];
  290. end;
  291. constructor TJSONParser.Create(Source: TJSONStringType; AUseUTF8 : Boolean = True);
  292. begin
  293. Inherited Create;
  294. FScanner:=TJSONScanner.Create(Source,[joUTF8]);
  295. if AUseUTF8 then
  296. Options:=Options + [joUTF8];
  297. end;
  298. constructor TJSONParser.Create(Source: TStream; AOptions: TJSONOptions);
  299. begin
  300. FScanner:=TJSONScanner.Create(Source,AOptions);
  301. end;
  302. constructor TJSONParser.Create(const Source: String; AOptions: TJSONOptions);
  303. begin
  304. FScanner:=TJSONScanner.Create(Source,AOptions);
  305. end;
  306. destructor TJSONParser.Destroy();
  307. begin
  308. FreeAndNil(FScanner);
  309. inherited Destroy();
  310. end;
  311. Procedure InitJSONHandler;
  312. begin
  313. if GetJSONParserHandler=Nil then
  314. SetJSONParserHandler(@DefJSONParserHandler);
  315. end;
  316. Procedure DoneJSONHandler;
  317. begin
  318. if GetJSONParserHandler=@DefJSONParserHandler then
  319. SetJSONParserHandler(Nil);
  320. end;
  321. initialization
  322. InitJSONHandler;
  323. finalization
  324. DoneJSONHandler;
  325. end.