jsonparser.pp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  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. FuseUTF8,
  23. FStrict: Boolean;
  24. function ParseNumber: TJSONNumber;
  25. procedure SetStrict(const AValue: Boolean);
  26. function GetUTF8 : Boolean;
  27. procedure SetUTF8(const AValue: Boolean);
  28. Protected
  29. procedure DoError(const Msg: String);
  30. function DoParse(AtCurrent,AllowEOF: Boolean): TJSONData;
  31. function GetNextToken: TJSONToken;
  32. function CurrentTokenString: String;
  33. function CurrentToken: TJSONToken;
  34. function ParseArray: TJSONArray;
  35. function ParseObject: TJSONObject;
  36. Property Scanner : TJSONScanner read FScanner;
  37. Public
  38. function Parse: TJSONData;
  39. Constructor Create(Source : TStream; AUseUTF8 : Boolean = True); overload;
  40. Constructor Create(Source : TJSONStringType; AUseUTF8 : Boolean = True); overload;
  41. destructor Destroy();override;
  42. // Use strict JSON: " for strings, object members are strings, not identifiers
  43. Property Strict : Boolean Read FStrict Write SetStrict;
  44. // if set to TRUE, then strings will be converted to UTF8 ansistrings, not system codepage ansistrings.
  45. Property UseUTF8 : Boolean Read GetUTF8 Write SetUTF8;
  46. end;
  47. EJSONParser = Class(EParserError);
  48. implementation
  49. Resourcestring
  50. SErrUnexpectedEOF = 'Unexpected EOF encountered.';
  51. SErrUnexpectedToken = 'Unexpected token (%s) encountered.';
  52. SErrExpectedColon = 'Expected colon (:), got token "%s".';
  53. SErrUnexpectedComma = 'Invalid comma encountered.';
  54. SErrEmptyElement = 'Empty element encountered.';
  55. SErrExpectedElementName = 'Expected element name, got token "%s"';
  56. SExpectedCommaorBraceClose = 'Expected , or ], got token "%s".';
  57. SErrInvalidNumber = 'Number is not an integer or real number: %s';
  58. { TJSONParser }
  59. Function TJSONParser.Parse : TJSONData;
  60. begin
  61. Result:=DoParse(False,True);
  62. end;
  63. {
  64. Consume next token and convert to JSON data structure.
  65. If AtCurrent is true, the current token is used. If false,
  66. a token is gotten from the scanner.
  67. If AllowEOF is false, encountering a tkEOF will result in an exception.
  68. }
  69. Function TJSONParser.CurrentToken : TJSONToken;
  70. begin
  71. Result:=FScanner.CurToken;
  72. end;
  73. Function TJSONParser.CurrentTokenString : String;
  74. begin
  75. If CurrentToken in [tkString,tkIdentifier,tkNumber] then
  76. Result:=FScanner.CurTokenString
  77. else
  78. Result:=TokenInfos[CurrentToken];
  79. end;
  80. Function TJSONParser.DoParse(AtCurrent,AllowEOF : Boolean) : TJSONData;
  81. var
  82. T : TJSONToken;
  83. begin
  84. Result:=nil;
  85. try
  86. If not AtCurrent then
  87. T:=GetNextToken
  88. else
  89. T:=FScanner.CurToken;
  90. Case T of
  91. tkEof : If Not AllowEof then
  92. DoError(SErrUnexpectedEOF);
  93. tkNull : Result:=TJSONNull.Create;
  94. tkTrue,
  95. tkFalse : Result:=TJSONBoolean.Create(t=tkTrue);
  96. tkString : Result:=TJSONString.Create(CurrentTokenString);
  97. tkCurlyBraceOpen : Result:=ParseObject;
  98. tkCurlyBraceClose : DoError(SErrUnexpectedToken);
  99. tkSQuaredBraceOpen : Result:=ParseArray;
  100. tkSQuaredBraceClose : DoError(SErrUnexpectedToken);
  101. tkNumber : Result:=ParseNumber;
  102. tkComma : DoError(SErrUnexpectedToken);
  103. end;
  104. except
  105. FreeAndNil(Result);
  106. Raise;
  107. end;
  108. end;
  109. // Creates the correct JSON number type, based on the current token.
  110. Function TJSONParser.ParseNumber : TJSONNumber;
  111. Var
  112. I : Integer;
  113. I64 : Int64;
  114. F : TJSONFloat;
  115. S : String;
  116. begin
  117. S:=CurrentTokenString;
  118. I:=0;
  119. If TryStrToInt64(S,I64) then
  120. Result:=TJSONInt64Number.Create(I64)
  121. Else If TryStrToInt(S,I) then
  122. Result:=TJSONIntegerNumber.Create(I)
  123. else
  124. begin
  125. I:=0;
  126. Val(S,F,I);
  127. If (I<>0) then
  128. DoError(SErrInvalidNumber);
  129. Result:=TJSONFloatNumber.Create(F);
  130. end;
  131. end;
  132. function TJSONParser.GetUTF8 : Boolean;
  133. begin
  134. if Assigned(FScanner) then
  135. Result:=FScanner.UseUTF8
  136. else
  137. Result:=FUseUTF8;
  138. end;
  139. procedure TJSONParser.SetUTF8(const AValue: Boolean);
  140. begin
  141. FUseUTF8:=AValue;
  142. if Assigned(FScanner) then
  143. FScanner.UseUTF8:=FUseUTF8;
  144. end;
  145. procedure TJSONParser.SetStrict(const AValue: Boolean);
  146. begin
  147. if (FStrict=AValue) then
  148. exit;
  149. FStrict:=AValue;
  150. If Assigned(FScanner) then
  151. FScanner.Strict:=Fstrict;
  152. end;
  153. // Current token is {, on exit current token is }
  154. Function TJSONParser.ParseObject : TJSONObject;
  155. Var
  156. T : TJSONtoken;
  157. E : TJSONData;
  158. N : String;
  159. begin
  160. Result:=TJSONObject.Create;
  161. Try
  162. T:=GetNextToken;
  163. While T<>tkCurlyBraceClose do
  164. begin
  165. If (T<>tkString) and (T<>tkIdentifier) then
  166. DoError(SErrExpectedElementName);
  167. N:=CurrentTokenString;
  168. T:=GetNextToken;
  169. If (T<>tkColon) then
  170. DoError(SErrExpectedColon);
  171. E:=DoParse(False,False);
  172. Result.Add(N,E);
  173. T:=GetNextToken;
  174. If Not (T in [tkComma,tkCurlyBraceClose]) then
  175. DoError(SExpectedCommaorBraceClose);
  176. If T=tkComma then
  177. T:=GetNextToken;
  178. end;
  179. Except
  180. FreeAndNil(Result);
  181. Raise;
  182. end;
  183. end;
  184. // Current token is [, on exit current token is ]
  185. Function TJSONParser.ParseArray : TJSONArray;
  186. Var
  187. T : TJSONtoken;
  188. E : TJSONData;
  189. LastComma : Boolean;
  190. begin
  191. Result:=TJSONArray.Create;
  192. LastComma:=False;
  193. Try
  194. Repeat
  195. T:=GetNextToken;
  196. If (T<>tkSquaredBraceClose) then
  197. begin
  198. E:=DoParse(True,False);
  199. If (E<>Nil) then
  200. Result.Add(E)
  201. else if (Result.Count>0) then
  202. DoError(SErrEmptyElement);
  203. T:=GetNextToken;
  204. If Not (T in [tkComma,tkSquaredBraceClose]) then
  205. DoError(SExpectedCommaorBraceClose);
  206. LastComma:=(t=TkComma);
  207. end;
  208. Until (T=tkSquaredBraceClose);
  209. If LastComma then // Test for ,] case
  210. DoError(SErrUnExpectedToken);
  211. Except
  212. FreeAndNil(Result);
  213. Raise;
  214. end;
  215. end;
  216. // Get next token, discarding whitespace
  217. Function TJSONParser.GetNextToken : TJSONToken ;
  218. begin
  219. Repeat
  220. Result:=FScanner.FetchToken;
  221. Until (Result<>tkWhiteSpace);
  222. end;
  223. Procedure TJSONParser.DoError(const Msg : String);
  224. Var
  225. S : String;
  226. begin
  227. S:=Format(Msg,[CurrentTokenString]);
  228. S:=Format('Error at line %d, Pos %d:',[FScanner.CurRow,FSCanner.CurColumn])+S;
  229. Raise EJSONParser.Create(S);
  230. end;
  231. constructor TJSONParser.Create(Source: TStream; AUseUTF8 : Boolean = True);
  232. begin
  233. Inherited Create;
  234. FScanner:=TJSONScanner.Create(Source);
  235. UseUTF8:=AUseUTF8;
  236. end;
  237. constructor TJSONParser.Create(Source: TJSONStringType; AUseUTF8 : Boolean = True);
  238. begin
  239. Inherited Create;
  240. FScanner:=TJSONScanner.Create(Source);
  241. UseUTF8:=AUseUTF8;
  242. end;
  243. destructor TJSONParser.Destroy();
  244. begin
  245. FreeAndNil(FScanner);
  246. inherited Destroy();
  247. end;
  248. end.