JSONDecoder.as 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /*
  2. Copyright (c) 2008, Adobe Systems Incorporated
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are
  6. met:
  7. * Redistributions of source code must retain the above copyright notice,
  8. this list of conditions and the following disclaimer.
  9. * Redistributions in binary form must reproduce the above copyright
  10. notice, this list of conditions and the following disclaimer in the
  11. documentation and/or other materials provided with the distribution.
  12. * Neither the name of Adobe Systems Incorporated nor the names of its
  13. contributors may be used to endorse or promote products derived from
  14. this software without specific prior written permission.
  15. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  16. IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  17. THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  18. PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  19. CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  20. EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  21. PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  22. PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  23. LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  24. NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. package com.adobe.serialization.json
  28. {
  29. public class JSONDecoder
  30. {
  31. /**
  32. * Flag indicating if the parser should be strict about the format
  33. * of the JSON string it is attempting to decode.
  34. */
  35. private var strict:Boolean;
  36. /** The value that will get parsed from the JSON string */
  37. private var value:*;
  38. /** The tokenizer designated to read the JSON string */
  39. private var tokenizer:JSONTokenizer;
  40. /** The current token from the tokenizer */
  41. private var token:JSONToken;
  42. /**
  43. * Constructs a new JSONDecoder to parse a JSON string
  44. * into a native object.
  45. *
  46. * @param s The JSON string to be converted
  47. * into a native object
  48. * @param strict Flag indicating if the JSON string needs to
  49. * strictly match the JSON standard or not.
  50. * @langversion ActionScript 3.0
  51. * @playerversion Flash 9.0
  52. * @tiptext
  53. */
  54. public function JSONDecoder( s:String, strict:Boolean )
  55. {
  56. this.strict = strict;
  57. tokenizer = new JSONTokenizer( s, strict );
  58. nextToken();
  59. value = parseValue();
  60. // Make sure the input stream is empty
  61. if ( strict && nextToken() != null )
  62. {
  63. tokenizer.parseError( "Unexpected characters left in input stream" );
  64. }
  65. }
  66. /**
  67. * Gets the internal object that was created by parsing
  68. * the JSON string passed to the constructor.
  69. *
  70. * @return The internal object representation of the JSON
  71. * string that was passed to the constructor
  72. * @langversion ActionScript 3.0
  73. * @playerversion Flash 9.0
  74. * @tiptext
  75. */
  76. public function getValue():*
  77. {
  78. return value;
  79. }
  80. /**
  81. * Returns the next token from the tokenzier reading
  82. * the JSON string
  83. */
  84. private function nextToken():JSONToken
  85. {
  86. return token = tokenizer.getNextToken();
  87. }
  88. /**
  89. * Attempt to parse an array.
  90. */
  91. private function parseArray():Array
  92. {
  93. // create an array internally that we're going to attempt
  94. // to parse from the tokenizer
  95. var a:Array = new Array();
  96. // grab the next token from the tokenizer to move
  97. // past the opening [
  98. nextToken();
  99. // check to see if we have an empty array
  100. if ( token.type == JSONTokenType.RIGHT_BRACKET )
  101. {
  102. // we're done reading the array, so return it
  103. return a;
  104. }
  105. // in non-strict mode an empty array is also a comma
  106. // followed by a right bracket
  107. else if ( !strict && token.type == JSONTokenType.COMMA )
  108. {
  109. // move past the comma
  110. nextToken();
  111. // check to see if we're reached the end of the array
  112. if ( token.type == JSONTokenType.RIGHT_BRACKET )
  113. {
  114. return a;
  115. }
  116. else
  117. {
  118. tokenizer.parseError( "Leading commas are not supported. Expecting ']' but found " + token.value );
  119. }
  120. }
  121. // deal with elements of the array, and use an "infinite"
  122. // loop because we could have any amount of elements
  123. while ( true )
  124. {
  125. // read in the value and add it to the array
  126. a.push( parseValue() );
  127. // after the value there should be a ] or a ,
  128. nextToken();
  129. if ( token.type == JSONTokenType.RIGHT_BRACKET )
  130. {
  131. // we're done reading the array, so return it
  132. return a;
  133. }
  134. else if ( token.type == JSONTokenType.COMMA )
  135. {
  136. // move past the comma and read another value
  137. nextToken();
  138. // Allow arrays to have a comma after the last element
  139. // if the decoder is not in strict mode
  140. if ( !strict )
  141. {
  142. // Reached ",]" as the end of the array, so return it
  143. if ( token.type == JSONTokenType.RIGHT_BRACKET )
  144. {
  145. return a;
  146. }
  147. }
  148. }
  149. else
  150. {
  151. tokenizer.parseError( "Expecting ] or , but found " + token.value );
  152. }
  153. }
  154. return null;
  155. }
  156. /**
  157. * Attempt to parse an object.
  158. */
  159. private function parseObject():Object
  160. {
  161. // create the object internally that we're going to
  162. // attempt to parse from the tokenizer
  163. var o:Object = new Object();
  164. // store the string part of an object member so
  165. // that we can assign it a value in the object
  166. var key:String
  167. // grab the next token from the tokenizer
  168. nextToken();
  169. // check to see if we have an empty object
  170. if ( token.type == JSONTokenType.RIGHT_BRACE )
  171. {
  172. // we're done reading the object, so return it
  173. return o;
  174. }
  175. // in non-strict mode an empty object is also a comma
  176. // followed by a right bracket
  177. else if ( !strict && token.type == JSONTokenType.COMMA )
  178. {
  179. // move past the comma
  180. nextToken();
  181. // check to see if we're reached the end of the object
  182. if ( token.type == JSONTokenType.RIGHT_BRACE )
  183. {
  184. return o;
  185. }
  186. else
  187. {
  188. tokenizer.parseError( "Leading commas are not supported. Expecting '}' but found " + token.value );
  189. }
  190. }
  191. // deal with members of the object, and use an "infinite"
  192. // loop because we could have any amount of members
  193. while ( true )
  194. {
  195. if ( token.type == JSONTokenType.STRING )
  196. {
  197. // the string value we read is the key for the object
  198. key = String( token.value );
  199. // move past the string to see what's next
  200. nextToken();
  201. // after the string there should be a :
  202. if ( token.type == JSONTokenType.COLON )
  203. {
  204. // move past the : and read/assign a value for the key
  205. nextToken();
  206. o[key] = parseValue();
  207. // move past the value to see what's next
  208. nextToken();
  209. // after the value there's either a } or a ,
  210. if ( token.type == JSONTokenType.RIGHT_BRACE )
  211. {
  212. // we're done reading the object, so return it
  213. return o;
  214. }
  215. else if ( token.type == JSONTokenType.COMMA )
  216. {
  217. // skip past the comma and read another member
  218. nextToken();
  219. // Allow objects to have a comma after the last member
  220. // if the decoder is not in strict mode
  221. if ( !strict )
  222. {
  223. // Reached ",}" as the end of the object, so return it
  224. if ( token.type == JSONTokenType.RIGHT_BRACE )
  225. {
  226. return o;
  227. }
  228. }
  229. }
  230. else
  231. {
  232. tokenizer.parseError( "Expecting } or , but found " + token.value );
  233. }
  234. }
  235. else
  236. {
  237. tokenizer.parseError( "Expecting : but found " + token.value );
  238. }
  239. }
  240. else
  241. {
  242. tokenizer.parseError( "Expecting string but found " + token.value );
  243. }
  244. }
  245. return null;
  246. }
  247. /**
  248. * Attempt to parse a value
  249. */
  250. private function parseValue():Object
  251. {
  252. // Catch errors when the input stream ends abruptly
  253. if ( token == null )
  254. {
  255. tokenizer.parseError( "Unexpected end of input" );
  256. }
  257. switch ( token.type )
  258. {
  259. case JSONTokenType.LEFT_BRACE:
  260. return parseObject();
  261. case JSONTokenType.LEFT_BRACKET:
  262. return parseArray();
  263. case JSONTokenType.STRING:
  264. case JSONTokenType.NUMBER:
  265. case JSONTokenType.TRUE:
  266. case JSONTokenType.FALSE:
  267. case JSONTokenType.NULL:
  268. return token.value;
  269. case JSONTokenType.NAN:
  270. if ( !strict )
  271. {
  272. return token.value;
  273. }
  274. else
  275. {
  276. tokenizer.parseError( "Unexpected " + token.value );
  277. }
  278. default:
  279. tokenizer.parseError( "Unexpected " + token.value );
  280. }
  281. return null;
  282. }
  283. }
  284. }