json.monkey2 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. #rem monkeydoc
  2. JSON (JavaScript Object Notation) is a lightweight data-interchange format.
  3. \## Reading JSON data
  4. Use the [[JsonObject.Load]] function to load JSON data from a file, or [[JsonObject.Parse]] to parse JSON data from a string.
  5. \## Writing JSON data
  6. To save a JSON object, use the [[JsonObject.Save]] function.
  7. #end
  8. Namespace std.json
  9. #rem monkeydoc JsonError class.
  10. #end
  11. Class JsonError Extends Throwable
  12. End
  13. #rem monkeydoc JsonValue class.
  14. This is base class of all JsonValue types.
  15. #end
  16. Class JsonValue Abstract
  17. Method ToBool:Bool() Virtual
  18. Assert( False )
  19. Return Null
  20. End
  21. Method ToNumber:Double() Virtual
  22. Assert( False )
  23. Return Null
  24. End
  25. Method ToString:String() Virtual
  26. Assert( False )
  27. Return Null
  28. End
  29. Method ToArray:Stack<JsonValue>() Virtual
  30. Assert( False )
  31. Return Null
  32. End
  33. Method ToObject:StringMap<JsonValue>() Virtual
  34. Assert( False )
  35. Return Null
  36. End
  37. Method ToJson:String() Virtual
  38. Local buf:=New StringStack
  39. PushJson( buf )
  40. Return buf.Join( "" )
  41. End
  42. Method ToInt:Int()
  43. Return Int( ToNumber() )
  44. End
  45. Method ToLong:Long()
  46. Return Long( ToNumber() )
  47. End
  48. Method ToFloat:Float()
  49. Return Float( ToNumber() )
  50. End
  51. Method ToDouble:Double()
  52. Return ToNumber()
  53. End
  54. Method Save:Bool( path:String )
  55. Local buf:=New StringStack
  56. PushJson( buf )
  57. Local src:=buf.Join( "" )
  58. Return stringio.SaveString( src,path )
  59. End
  60. Function Load:JsonValue( path:String,throwex:Bool=False )
  61. Local src:=stringio.LoadString( path )
  62. If Not src Return Null
  63. Return Parse( src,throwex )
  64. End
  65. Function Parse:JsonValue( src:String,throwex:Bool=False )
  66. Try
  67. Local parser:=New JsonParser( src )
  68. Return parser.ParseValue()
  69. Catch ex:JsonError
  70. If throwex Throw ex
  71. End
  72. Return Null
  73. End
  74. Protected
  75. Global _indent:String
  76. #rem monkeydoc @hidden
  77. #end
  78. Method PushJson:Void( buf:StringStack ) Virtual
  79. buf.Push( ToJson() )
  80. End
  81. End
  82. #rem monkeydoc JsonBool class.
  83. #end
  84. Class JsonBool Extends JsonValue
  85. Const TrueValue:JsonBool=New JsonBool( True )
  86. Const FalseValue:JsonBool=New JsonBool( False )
  87. Method New( data:Bool=False )
  88. _data=data
  89. End
  90. Property Data:Bool()
  91. Return _data
  92. Setter( data:Bool )
  93. _data=data
  94. End
  95. Method ToBool:Bool() Override
  96. Return _data
  97. End
  98. Method ToNumber:Double() Override
  99. Return _data
  100. End
  101. Method ToString:String() Override
  102. Return _data ? "true" Else "false"
  103. End
  104. Method ToJson:String() Override
  105. If _data Return "true"
  106. Return "false"
  107. End
  108. Private
  109. Field _data:Bool
  110. End
  111. #rem monkeydoc JsonNumber class.
  112. #end
  113. Class JsonNumber Extends JsonValue
  114. Method New( data:Double=0 )
  115. _data=data
  116. End
  117. Property Data:Double()
  118. Return _data
  119. Setter( data:Double )
  120. _data=data
  121. End
  122. Method ToBool:Bool() Override
  123. Return _data
  124. End
  125. Method ToNumber:Double() Override
  126. Return _data
  127. End
  128. Method ToString:String() Override
  129. Return _data
  130. End
  131. Method ToJson:String() Override
  132. Return String( _data )
  133. End
  134. Private
  135. Field _data:Double
  136. End
  137. #rem monkeydoc JsonString class.
  138. #end
  139. Class JsonString Extends JsonValue
  140. Method New( data:String="" )
  141. _data=data
  142. End
  143. Property Data:String()
  144. Return _data
  145. Setter( data:String )
  146. _data=data
  147. End
  148. Method ToBool:Bool() Override
  149. Return _data
  150. End
  151. Method ToNumber:Double() Override
  152. Return Double( _data )
  153. End
  154. Method ToString:String() Override
  155. Return _data
  156. End
  157. Method ToJson:String() Override
  158. Return "~q"+_data.Replace( "~q","\~q" )+"~q"
  159. End
  160. Private
  161. Field _data:String
  162. End
  163. #rem monkeydoc JsonArray class.
  164. #end
  165. Class JsonArray Extends JsonValue
  166. Method New( length:Int=0 )
  167. _data=New Stack<JsonValue>( length )
  168. End
  169. Method New( data:JsonValue[] )
  170. _data=New Stack<JsonValue>( data )
  171. End
  172. Method New( data:Stack<JsonValue> )
  173. _data=data
  174. End
  175. Property Data:Stack<JsonValue>()
  176. Return _data
  177. Setter( data:Stack<JsonValue> )
  178. _data=data
  179. End
  180. Property Length:Int()
  181. Return _data.Length
  182. End
  183. Operator[]:JsonValue( index:Int )
  184. DebugAssert( index>=0 )
  185. If index<_data.Length Return _data[index]
  186. Return Null
  187. End
  188. Operator[]=( index:Int,value:JsonValue )
  189. DebugAssert( index>=0 )
  190. If index>=_data.Length _data.Resize( index+1 )
  191. _data[index]=value
  192. End
  193. Method Push( value:JsonValue )
  194. _data.Push( value )
  195. End
  196. Method Add( value:JsonValue )
  197. _data.Add( value )
  198. End
  199. Method ToArray:Stack<JsonValue>() Override
  200. Return _data
  201. End
  202. Private
  203. Field _data:Stack<JsonValue>
  204. Method PushJson:Void( buf:StringStack ) Override
  205. buf.Push( "[" )
  206. Local indent:=_indent
  207. Local n:=0
  208. For Local value:=Eachin _data
  209. If n
  210. buf.Push( "," )
  211. Endif
  212. n+=1
  213. If value
  214. buf.Push( value.ToJson() )
  215. Else
  216. buf.Push( "null" )
  217. Endif
  218. Next
  219. _indent=indent
  220. buf.Push( "]" )
  221. End
  222. End
  223. #rem monkeydoc JsonObject class.
  224. #end
  225. Class JsonObject Extends JsonValue
  226. Method New( data:StringMap<JsonValue> =Null )
  227. If Not data data=New StringMap<JsonValue>
  228. _data=data
  229. End
  230. Property Data:StringMap<JsonValue>()
  231. Return _data
  232. Setter( data:StringMap<JsonValue> )
  233. _data=data
  234. End
  235. Method Contains:Bool( key:String )
  236. Return _data.Contains( key )
  237. End
  238. Operator[]:JsonValue( key:String )
  239. Return _data[key]
  240. End
  241. Operator[]=( key:String,value:JsonValue )
  242. _data[key]=value
  243. End
  244. Method ToObject:StringMap<JsonValue>() Override
  245. Return Data
  246. End
  247. Function Load:JsonObject( path:String,throwex:Bool=False )
  248. Local src:=std.stringio.LoadString( path )
  249. If Not src Return Null
  250. Return Parse( src,throwex )
  251. End
  252. Function Parse:JsonObject( json:String,throwex:Bool=False )
  253. Try
  254. Local parser:=New JsonParser( json )
  255. Return New JsonObject( parser.ParseObject() )
  256. Catch ex:JsonError
  257. If throwex Throw ex
  258. End
  259. Return Null
  260. End
  261. Private
  262. Field _data:StringMap<JsonValue>
  263. Method PushJson:Void( buf:StringStack ) Override
  264. buf.Push( "{~n" )
  265. _indent+="~t"
  266. Local t:=False
  267. For Local it:=Eachin _data
  268. If t buf.Push( ",~n" )
  269. buf.Push( _indent+"~q"+it.Key.Replace( "~q","\~q" )+"~q:" )
  270. If it.Value
  271. buf.Push( it.Value.ToJson() )
  272. Else
  273. buf.Push( "null" )
  274. Endif
  275. t=True
  276. Next
  277. _indent=_indent.Slice( 0,-1 )
  278. buf.Push( "~n"+_indent+"}" )
  279. End
  280. End
  281. #rem monkeydoc JsonParser class.
  282. #end
  283. Class JsonParser
  284. Method New( json:String )
  285. _text=json
  286. Bump()
  287. End
  288. Method ParseValue:JsonValue()
  289. If TokeType=T_STRING Return New JsonString( ParseString() )
  290. If TokeType=T_NUMBER Return New JsonNumber( ParseNumber() )
  291. If Toke="{" Return New JsonObject( ParseObject() )
  292. If Toke="[" Return New JsonArray( ParseArray() )
  293. If CParse( "true" ) Return JsonBool.TrueValue
  294. If CParse( "false" ) Return JsonBool.FalseValue
  295. If CParse( "null" ) Return Null
  296. Return Null
  297. End
  298. Private
  299. Const T_EOF:=0
  300. Const T_STRING:=1
  301. Const T_NUMBER:=2
  302. Const T_SYMBOL:=3
  303. Const T_IDENT:=4
  304. Field _text:String
  305. Field _toke:String
  306. Field _type:Int
  307. Field _pos:Int
  308. Method GetChar:Int()
  309. If _pos=_text.Length Throw New JsonError()
  310. _pos+=1
  311. Return _text[_pos-1]
  312. End
  313. Method PeekChar:Int()
  314. If _pos=_text.Length Return 0
  315. Return _text[_pos]
  316. End
  317. Method ParseChar:Void( chr:Int )
  318. If _pos>=_text.Length Or _text[_pos]<>chr Throw New JsonError()
  319. _pos+=1
  320. End
  321. Method CParseChar:Bool( chr:Int )
  322. If _pos>=_text.Length Or _text[_pos]<>chr Return False
  323. _pos+=1
  324. Return True
  325. End
  326. Method CParseDigits:Bool()
  327. Local p:=_pos
  328. While _pos<_text.Length And _text[_pos]>=48 And _text[_pos]<=57
  329. _pos+=1
  330. Wend
  331. Return _pos>p
  332. End
  333. Method Bump:String()
  334. While _pos<_text.Length
  335. If _text[_pos]=47
  336. '// comment? - can't live without 'em!
  337. If _pos+1=_text.Length Or _text[_pos+1]<>47 Exit
  338. _pos+=2
  339. While _pos<_text.Length And _text[_pos]<>10
  340. _pos+=1
  341. Wend
  342. If _pos<_text.Length _pos+=1
  343. Else
  344. If _text[_pos]>32 Exit
  345. _pos+=1
  346. Endif
  347. Wend
  348. If _pos=_text.Length
  349. _toke=""
  350. _type=T_EOF
  351. Return _toke
  352. Endif
  353. Local pos:=_pos
  354. Local chr:=GetChar()
  355. If chr=34
  356. Repeat
  357. Local chr:=GetChar()
  358. If chr=34 Exit
  359. If chr=92 GetChar()
  360. Forever
  361. _type=T_STRING
  362. Else If chr=39
  363. Repeat
  364. Local chr:=GetChar()
  365. If chr=39 Exit
  366. If chr=92 GetChar()
  367. Forever
  368. _type=T_STRING
  369. Else If (chr>=48 And chr<=57) Or chr=45
  370. If chr=45 '-
  371. chr=GetChar()
  372. If chr<48 Or chr>57 Throw New JsonError()
  373. Endif
  374. If chr<>48 '0
  375. CParseDigits()
  376. End
  377. If CParseChar( 46 ) '.
  378. CParseDigits()
  379. Endif
  380. If CParseChar( 69 ) Or CParseChar( 101 ) 'e E
  381. If PeekChar()=43 Or PeekChar()=45 GetChar() '+ -
  382. If Not CParseDigits() Throw New JsonError()
  383. Endif
  384. _type=T_NUMBER
  385. Else If (chr>=65 And chr<91) Or (chr>=97 And chr<123) Or chr=95
  386. chr=PeekChar()
  387. While (chr>=65 And chr<91) Or (chr>=97 And chr<123) Or (chr>=48 And chr<58) Or chr=95
  388. GetChar()
  389. chr=PeekChar()
  390. Wend
  391. _type=T_IDENT
  392. Else
  393. _type=T_SYMBOL
  394. Endif
  395. _toke=_text.Slice( pos,_pos )
  396. Return _toke
  397. End
  398. Property Toke:String()
  399. Return _toke
  400. End
  401. Property TokeType:Int()
  402. Return _type
  403. End
  404. Method CParse:Bool( toke:String )
  405. If toke<>_toke Return False
  406. Bump()
  407. Return True
  408. End
  409. Method Parse:Void( toke:String )
  410. If Not CParse( toke ) Throw New JsonError()
  411. End
  412. Method ParseObject:StringMap<JsonValue>()
  413. Parse( "{" )
  414. Local map:=New StringMap<JsonValue>
  415. If CParse( "}" ) Return map
  416. Repeat
  417. Local name:=Toke
  418. If TokeType=T_IDENT
  419. Bump()
  420. Else
  421. name=ParseString()
  422. Endif
  423. Parse( ":" )
  424. Local value:=ParseValue()
  425. map.Set( name,value )
  426. Until Not CParse( "," )
  427. Parse( "}" )
  428. Return map
  429. End
  430. Method ParseArray:Stack<JsonValue>()
  431. Parse( "[" )
  432. Local stack:=New Stack<JsonValue>
  433. If CParse( "]" ) Return stack
  434. Repeat
  435. Local value:=ParseValue()
  436. stack.Add( value )
  437. Until Not CParse( "," )
  438. Parse( "]" )
  439. Return stack
  440. End
  441. Method ParseString:String()
  442. If TokeType<>T_STRING Throw New JsonError()
  443. Local toke:=Toke.Slice( 1,-1 )
  444. Local i:=toke.Find( "\" )
  445. If i<>-1
  446. Local frags:=New StringStack,p:=0,esc:=""
  447. Repeat
  448. If i+1>=toke.Length Throw New JsonError()
  449. frags.Push( toke.Slice( p,i ) )
  450. Select toke[i+1]
  451. Case 34 esc="~q" '\"
  452. Case 92 esc="\" '\\
  453. Case 47 esc="/" '\/
  454. Case 98 esc=String.FromChar( 8 ) '\b
  455. Case 102 esc=String.FromChar( 12 ) '\f
  456. Case 114 esc=String.FromChar( 13 ) '\r
  457. Case 110 esc=String.FromChar( 10 ) '\n
  458. Case 117 '\uxxxx
  459. If i+6>toke.Length Throw New JsonError()
  460. Local val:=0
  461. For Local j:=2 Until 6
  462. Local chr:=toke[i+j]
  463. If chr>=48 And chr<58
  464. val=val Shl 4 | (chr-48)
  465. Else If chr>=65 And chr<123
  466. chr&=31
  467. If chr<1 Or chr>6 Throw New JsonError()
  468. val=val Shl 4 | (chr+9)
  469. Else
  470. Throw New JsonError()
  471. Endif
  472. Next
  473. esc=String.FromChar( val )
  474. i+=4
  475. Default
  476. Throw New JsonError()
  477. End
  478. frags.Push( esc )
  479. p=i+2
  480. i=toke.Find( "\",p )
  481. If i<>-1 Continue
  482. frags.Push( toke.Slice( p ) )
  483. Exit
  484. Forever
  485. toke=frags.Join( "" )
  486. Endif
  487. Bump()
  488. Return toke
  489. End
  490. Method ParseNumber:Double()
  491. If TokeType<>T_NUMBER Throw New JsonError()
  492. Local toke:=Toke
  493. Bump()
  494. Return Double( toke )
  495. End
  496. End