json 0.4.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of json might be problematic. Click here for more details.

Files changed (74) hide show
  1. data/CHANGES +6 -1
  2. data/README +49 -7
  3. data/Rakefile +216 -52
  4. data/TODO +1 -0
  5. data/VERSION +1 -1
  6. data/benchmarks/benchmark.txt +133 -0
  7. data/benchmarks/benchmark_generator.rb +44 -0
  8. data/benchmarks/benchmark_parser.rb +22 -0
  9. data/benchmarks/benchmark_rails.rb +26 -0
  10. data/data/example.json +1 -0
  11. data/data/index.html +37 -0
  12. data/data/prototype.js +2515 -0
  13. data/ext/json/ext/generator/Makefile +149 -0
  14. data/ext/json/ext/generator/extconf.rb +9 -0
  15. data/ext/json/ext/generator/generator.c +729 -0
  16. data/ext/json/ext/generator/unicode.c +184 -0
  17. data/ext/json/ext/generator/unicode.h +40 -0
  18. data/ext/json/ext/parser/Makefile +149 -0
  19. data/ext/json/ext/parser/extconf.rb +9 -0
  20. data/ext/json/ext/parser/parser.c +1551 -0
  21. data/ext/json/ext/parser/parser.rl +515 -0
  22. data/ext/json/ext/parser/unicode.c +156 -0
  23. data/ext/json/ext/parser/unicode.h +44 -0
  24. data/install.rb +13 -8
  25. data/lib/json.rb +101 -614
  26. data/lib/json/common.rb +184 -0
  27. data/lib/json/editor.rb +19 -10
  28. data/lib/json/ext.rb +13 -0
  29. data/lib/json/pure.rb +75 -0
  30. data/lib/json/pure/generator.rb +321 -0
  31. data/lib/json/pure/parser.rb +210 -0
  32. data/lib/json/version.rb +8 -0
  33. data/tests/fixtures/fail1.json +1 -0
  34. data/tests/fixtures/fail10.json +1 -0
  35. data/tests/fixtures/fail11.json +1 -0
  36. data/tests/fixtures/fail12.json +1 -0
  37. data/tests/fixtures/fail13.json +1 -0
  38. data/tests/fixtures/fail14.json +1 -0
  39. data/tests/fixtures/fail15.json +1 -0
  40. data/tests/fixtures/fail16.json +1 -0
  41. data/tests/fixtures/fail17.json +1 -0
  42. data/tests/fixtures/fail19.json +1 -0
  43. data/tests/fixtures/fail2.json +1 -0
  44. data/tests/fixtures/fail20.json +1 -0
  45. data/tests/fixtures/fail21.json +1 -0
  46. data/tests/fixtures/fail22.json +1 -0
  47. data/tests/fixtures/fail23.json +1 -0
  48. data/tests/fixtures/fail24.json +1 -0
  49. data/tests/fixtures/fail25.json +1 -0
  50. data/tests/fixtures/fail26.json +1 -0
  51. data/tests/fixtures/fail27.json +2 -0
  52. data/tests/fixtures/fail28.json +2 -0
  53. data/tests/fixtures/fail3.json +1 -0
  54. data/tests/fixtures/fail4.json +1 -0
  55. data/tests/fixtures/fail5.json +1 -0
  56. data/tests/fixtures/fail6.json +1 -0
  57. data/tests/fixtures/fail7.json +1 -0
  58. data/tests/fixtures/fail8.json +1 -0
  59. data/tests/fixtures/fail9.json +1 -0
  60. data/tests/fixtures/pass1.json +56 -0
  61. data/tests/fixtures/pass18.json +1 -0
  62. data/tests/fixtures/pass2.json +1 -0
  63. data/tests/fixtures/pass3.json +6 -0
  64. data/tests/runner.rb +8 -2
  65. data/tests/test_json.rb +102 -154
  66. data/tests/test_json_addition.rb +94 -0
  67. data/tests/test_json_fixtures.rb +30 -0
  68. data/tests/test_json_generate.rb +81 -0
  69. data/tests/test_json_unicode.rb +55 -0
  70. data/tools/fuzz.rb +133 -0
  71. data/tools/server.rb +62 -0
  72. metadata +87 -10
  73. data/bla.json.tmp +0 -0
  74. data/lib/json.rb.orig +0 -708
metadata CHANGED
@@ -1,12 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.0
2
+ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: json
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.4.3
7
- date: 2007-02-10 00:00:00 +01:00
8
- summary: A JSON implementation in Ruby
6
+ version: 1.0.0
7
+ date: 2007-03-27 00:00:00 +02:00
8
+ summary: A JSON implementation as a Ruby extension
9
9
  require_paths:
10
+ - ext/json/ext
11
+ - ext
10
12
  - lib
11
13
  email: flori@ping.de
12
14
  homepage: http://json.rubyforge.org
@@ -35,37 +37,112 @@ files:
35
37
  - tests
36
38
  - GPL
37
39
  - install.rb
40
+ - ext
41
+ - diagrams
42
+ - benchmarks
38
43
  - Rakefile
39
- - bla.json.tmp
44
+ - data
40
45
  - lib
41
46
  - README
47
+ - tools
42
48
  - CHANGES
43
49
  - bin/edit_json.rb
50
+ - tests/test_json_fixtures.rb
44
51
  - tests/runner.rb
45
52
  - tests/test_json.rb
46
- - lib/json.rb.orig
53
+ - tests/fixtures
54
+ - tests/test_json_unicode.rb
55
+ - tests/test_json_generate.rb
56
+ - tests/test_json_addition.rb
57
+ - tests/fixtures/fail27.json
58
+ - tests/fixtures/fail22.json
59
+ - tests/fixtures/fail26.json
60
+ - tests/fixtures/fail16.json
61
+ - tests/fixtures/fail28.json
62
+ - tests/fixtures/fail25.json
63
+ - tests/fixtures/pass18.json
64
+ - tests/fixtures/fail9.json
65
+ - tests/fixtures/fail20.json
66
+ - tests/fixtures/fail24.json
67
+ - tests/fixtures/fail14.json
68
+ - tests/fixtures/fail4.json
69
+ - tests/fixtures/fail7.json
70
+ - tests/fixtures/fail10.json
71
+ - tests/fixtures/fail13.json
72
+ - tests/fixtures/fail6.json
73
+ - tests/fixtures/fail21.json
74
+ - tests/fixtures/fail23.json
75
+ - tests/fixtures/fail3.json
76
+ - tests/fixtures/fail1.json
77
+ - tests/fixtures/fail11.json
78
+ - tests/fixtures/fail5.json
79
+ - tests/fixtures/pass1.json
80
+ - tests/fixtures/fail12.json
81
+ - tests/fixtures/fail15.json
82
+ - tests/fixtures/pass3.json
83
+ - tests/fixtures/fail8.json
84
+ - tests/fixtures/fail17.json
85
+ - tests/fixtures/fail19.json
86
+ - tests/fixtures/pass2.json
87
+ - tests/fixtures/fail2.json
88
+ - ext/json
89
+ - ext/json/ext
90
+ - ext/json/ext/generator
91
+ - ext/json/ext/parser
92
+ - ext/json/ext/generator/Makefile
93
+ - ext/json/ext/generator/unicode.c
94
+ - ext/json/ext/generator/unicode.h
95
+ - ext/json/ext/generator/extconf.rb
96
+ - ext/json/ext/generator/generator.c
97
+ - ext/json/ext/parser/Makefile
98
+ - ext/json/ext/parser/parser.c
99
+ - ext/json/ext/parser/unicode.c
100
+ - ext/json/ext/parser/parser.rl
101
+ - ext/json/ext/parser/unicode.h
102
+ - ext/json/ext/parser/extconf.rb
103
+ - benchmarks/benchmark_generator.rb
104
+ - benchmarks/benchmark.txt
105
+ - benchmarks/benchmark_parser.rb
106
+ - benchmarks/benchmark_rails.rb
107
+ - data/example.json
108
+ - data/prototype.js
109
+ - data/index.html
47
110
  - lib/json
48
111
  - lib/json.rb
49
112
  - lib/json/FalseClass.xpm
50
113
  - lib/json/TrueClass.xpm
114
+ - lib/json/ext.rb
115
+ - lib/json/common.rb
51
116
  - lib/json/Hash.xpm
117
+ - lib/json/pure
52
118
  - lib/json/Key.xpm
53
119
  - lib/json/Numeric.xpm
54
120
  - lib/json/Array.xpm
55
121
  - lib/json/editor.rb
56
122
  - lib/json/String.xpm
123
+ - lib/json/pure.rb
57
124
  - lib/json/NilClass.xpm
125
+ - lib/json/version.rb
58
126
  - lib/json/json.xpm
127
+ - lib/json/pure/parser.rb
128
+ - lib/json/pure/generator.rb
129
+ - tools/server.rb
130
+ - tools/fuzz.rb
59
131
  test_files:
60
132
  - tests/runner.rb
61
- rdoc_options: []
62
-
133
+ rdoc_options:
134
+ - --title
135
+ - JSON -- A JSON implemention
136
+ - --main
137
+ - JSON
138
+ - --line-numbers
63
139
  extra_rdoc_files: []
64
140
 
65
141
  executables:
66
142
  - edit_json.rb
67
- extensions: []
68
-
143
+ extensions:
144
+ - ext/json/ext/parser/extconf.rb
145
+ - ext/json/ext/generator/extconf.rb
69
146
  requirements: []
70
147
 
71
148
  dependencies: []
File without changes
@@ -1,708 +0,0 @@
1
- # = json - JSON library for Ruby
2
- #
3
- # == Description
4
- #
5
- # == Author
6
- #
7
- # Florian Frank <mailto:flori@ping.de>
8
- #
9
- # == License
10
- #
11
- # This is free software; you can redistribute it and/or modify it under the
12
- # terms of the GNU General Public License Version 2 as published by the Free
13
- # Software Foundation: www.gnu.org/copyleft/gpl.html
14
- #
15
- # == Download
16
- #
17
- # The latest version of this library can be downloaded at
18
- #
19
- # * http://rubyforge.org/frs?group_id=953
20
- #
21
- # Online Documentation should be located at
22
- #
23
- # * http://json.rubyforge.org
24
- #
25
- # == Examples
26
- #
27
- # To create a JSON string from a ruby data structure, you
28
- # can call JSON.unparse like that:
29
- #
30
- # json = JSON.unparse [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
31
- # # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
32
- #
33
- # It's also possible to call the #to_json method directly.
34
- #
35
- # json = [1, 2, {"a"=>3.141}, false, true, nil, 4..10].to_json
36
- # # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
37
- #
38
- # To get back a ruby data structure, you have to call
39
- # JSON.parse on the JSON string:
40
- #
41
- # JSON.parse json
42
- # # => [1, 2, {"a"=>3.141}, false, true, nil, "4..10"]
43
- #
44
- # Note, that the range from the original data structure is a simple
45
- # string now. The reason for this is, that JSON doesn't support ranges
46
- # or arbitrary classes. In this case the json library falls back to call
47
- # Object#to_json, which is the same as #to_s.to_json.
48
- #
49
- # It's possible to extend JSON to support serialization of arbitray classes by
50
- # simply implementing a more specialized version of the #to_json method, that
51
- # should return a JSON object (a hash converted to JSON with #to_json)
52
- # like this (don't forget the *a for all the arguments):
53
- #
54
- # class Range
55
- # def to_json(*a)
56
- # {
57
- # 'json_class' => self.class.name,
58
- # 'data' => [ first, last, exclude_end? ]
59
- # }.to_json(*a)
60
- # end
61
- # end
62
- #
63
- # The hash key 'json_class' is the class, that will be asked to deserialize the
64
- # JSON representation later. In this case it's 'Range', but any namespace of
65
- # the form 'A::B' or '::A::B' will do. All other keys are arbitrary and can be
66
- # used to store the necessary data to configure the object to be deserialized.
67
- #
68
- # If a the key 'json_class' is found in a JSON object, the JSON parser checks
69
- # if the given class responds to the json_create class method. If so, it is
70
- # called with the JSON object converted to a Ruby hash. So a range can
71
- # be deserialized by implementing Range.json_create like this:
72
- #
73
- # class Range
74
- # def self.json_create(o)
75
- # new(*o['data'])
76
- # end
77
- # end
78
- #
79
- # Now it possible to serialize/deserialize ranges as well:
80
- #
81
- # json = JSON.unparse [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
82
- # # => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
83
- # JSON.parse json
84
- # # => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
85
- #
86
- # JSON.unparse always creates the shortes possible string representation of a
87
- # ruby data structure in one line. This good for data storage or network
88
- # protocols, but not so good for humans to read. Fortunately there's
89
- # also JSON.pretty_unparse that creates a more readable output:
90
- #
91
- # puts JSON.pretty_unparse([1, 2, {"a"=>3.141}, false, true, nil, 4..10])
92
- # [
93
- # 1,
94
- # 2,
95
- # {
96
- # "a": 3.141
97
- # },
98
- # false,
99
- # true,
100
- # null,
101
- # {
102
- # "json_class": "Range",
103
- # "data": [
104
- # 4,
105
- # 10,
106
- # false
107
- # ]
108
- # }
109
- # ]
110
- #
111
- # There are also the methods Kernel#j for unparse, and Kernel#jj for
112
- # pretty_unparse output to the console, that work analogous to Kernel#p and
113
- # Kernel#pp.
114
- #
115
-
116
- require 'strscan'
117
-
118
- # This module is the namespace for all the JSON related classes. It also
119
- # defines some module functions to expose a nicer API to users, instead
120
- # of using the parser and other classes directly.
121
- module JSON
122
- # The base exception for JSON errors.
123
- JSONError = Class.new StandardError
124
-
125
- # This exception is raise, if a parser error occurs.
126
- ParserError = Class.new JSONError
127
-
128
- # This exception is raise, if a unparser error occurs.
129
- UnparserError = Class.new JSONError
130
-
131
- # If a circular data structure is encountered while unparsing
132
- # this exception is raised.
133
- CircularDatastructure = Class.new UnparserError
134
-
135
- class << self
136
- # Switches on Unicode support, if _enable_ is _true_. Otherwise switches
137
- # Unicode support off.
138
- def support_unicode=(enable)
139
- @support_unicode = enable
140
- end
141
-
142
- # Returns _true_ if JSON supports unicode, otherwise _false_ is returned.
143
- #
144
- # If loading of the iconv library fails, or it doesn't support utf8/utf16
145
- # encoding, this will be set to false, as a fallback.
146
- def support_unicode?
147
- !!@support_unicode
148
- end
149
- end
150
- JSON.support_unicode = true # default, however it's possible to switch off
151
- # full unicode support, if non-ascii bytes should be
152
- # just passed through.
153
-
154
- begin
155
- require 'iconv'
156
- # An iconv instance to convert from UTF8 to UTF16 Big Endian.
157
- UTF16toUTF8 = Iconv.new('utf-8', 'utf-16be')
158
- # An iconv instance to convert from UTF16 Big Endian to UTF8.
159
- UTF8toUTF16 = Iconv.new('utf-16be', 'utf-8'); UTF8toUTF16.iconv('no bom')
160
- rescue Errno::EINVAL
161
- begin
162
- old_verbose = $VERBOSE
163
- $VERBOSE = nil
164
- # An iconv instance to convert from UTF8 to UTF16 Big Endian.
165
- UTF16toUTF8 = Iconv.new('utf-8', 'utf-16')
166
- # An iconv instance to convert from UTF16 Big Endian to UTF8.
167
- UTF8toUTF16 = Iconv.new('utf-16', 'utf-8'); UTF8toUTF16.iconv('no bom')
168
- if UTF8toUTF16.iconv("\xe2\x82\xac") == "\xac\x20"
169
- swapper = Class.new do
170
- def initialize(iconv)
171
- @iconv = iconv
172
- end
173
-
174
- def iconv(string)
175
- result = @iconv.iconv(string)
176
- JSON.swap!(result)
177
- end
178
- end
179
- UTF8toUTF16 = swapper.new(UTF8toUTF16)
180
- end
181
- if UTF16toUTF8.iconv("\xac\x20") == "\xe2\x82\xac"
182
- swapper = Class.new do
183
- def initialize(iconv)
184
- @iconv = iconv
185
- end
186
-
187
- def iconv(string)
188
- string = JSON.swap!(string.dup)
189
- @iconv.iconv(string)
190
- end
191
- end
192
- UTF16toUTF8 = swapper.new(UTF16toUTF8)
193
- end
194
- rescue Errno::EINVAL
195
- # Enforce disabling of unicode support, if iconv doesn't support
196
- # UTF8/UTF16 at all.
197
- JSON.support_unicode = false
198
- ensure
199
- $VERBOSE = old_verbose
200
- end
201
- rescue LoadError
202
- # Enforce disabling of unicode support, if iconv doesn't exist.
203
- JSON.support_unicode = false
204
- end
205
-
206
- # Swap consecutive bytes in string in place.
207
- def self.swap!(string)
208
- 0.upto(string.size / 2) do |i|
209
- break unless string[2 * i + 1]
210
- string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
211
- end
212
- string
213
- end
214
-
215
- # This class implements the JSON parser that is used to parse a JSON string
216
- # into a Ruby data structure.
217
- class Parser < StringScanner
218
- STRING = /"((?:[^"\\]|\\.)*)"/
219
- INTEGER = /-?\d+/
220
- FLOAT = /-?\d+\.(\d*)(?i:e[+-]?\d+)?/
221
- OBJECT_OPEN = /\{/
222
- OBJECT_CLOSE = /\}/
223
- ARRAY_OPEN = /\[/
224
- ARRAY_CLOSE = /\]/
225
- PAIR_DELIMITER = /:/
226
- COLLECTION_DELIMITER = /,/
227
- TRUE = /true/
228
- FALSE = /false/
229
- NULL = /null/
230
- IGNORE = %r(
231
- (?:
232
- //[^\n\r]*[\n\r]| # line comments
233
- /\* # c-style comments
234
- (?:
235
- [^*/]| # normal chars
236
- /[^*]| # slashes that do not start a nested comment
237
- \*[^/]| # asterisks that do not end this comment
238
- /(?=\*/) # single slash before this comment's end
239
- )*
240
- \*/ # the end of this comment
241
- |\s+ # whitespaces
242
- )+
243
- )mx
244
-
245
- UNPARSED = Object.new
246
-
247
- # Parses the current JSON string and returns the complete data structure
248
- # as a result.
249
- def parse
250
- reset
251
- until eos?
252
- case
253
- when scan(ARRAY_OPEN)
254
- return parse_array
255
- when scan(OBJECT_OPEN)
256
- return parse_object
257
- when skip(IGNORE)
258
- ;
259
- when !((value = parse_value).equal? UNPARSED)
260
- return value
261
- else
262
- raise ParserError, "source '#{peek(20)}' not in JSON!"
263
- end
264
- end
265
- end
266
-
267
- private
268
-
269
- def parse_string
270
- if scan(STRING)
271
- return '' if self[1].empty?
272
- self[1].gsub(%r(\\(?:[\\bfnrt"/]|u([A-Fa-f\d]{4})))) do
273
- case $~[0]
274
- when '\\\\' then '\\'
275
- when '\\b' then "\b"
276
- when '\\f' then "\f"
277
- when '\\n' then "\n"
278
- when '\\r' then "\r"
279
- when '\\t' then "\t"
280
- when '\\"' then '"'
281
- when '\\/' then '/'
282
- else
283
- if JSON.support_unicode? and $KCODE == 'UTF8'
284
- JSON.utf16_to_utf8($~[1])
285
- else
286
- # if utf8 mode is switched off or unicode not supported, try to
287
- # transform unicode \u-notation to bytes directly:
288
- $~[1].to_i(16).chr
289
- end
290
- end
291
- end
292
- else
293
- UNPARSED
294
- end
295
- end
296
-
297
- def parse_value
298
- case
299
- when scan(FLOAT)
300
- Float(self[0])
301
- when scan(INTEGER)
302
- Integer(self[0])
303
- when scan(TRUE)
304
- true
305
- when scan(FALSE)
306
- false
307
- when scan(NULL)
308
- nil
309
- when (string = parse_string) != UNPARSED
310
- string
311
- when scan(ARRAY_OPEN)
312
- parse_array
313
- when scan(OBJECT_OPEN)
314
- parse_object
315
- else
316
- UNPARSED
317
- end
318
- end
319
-
320
- def parse_array
321
- result = []
322
- until eos?
323
- case
324
- when (value = parse_value) != UNPARSED
325
- result << value
326
- skip(IGNORE)
327
- unless scan(COLLECTION_DELIMITER) or match?(ARRAY_CLOSE)
328
- raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
329
- end
330
- when scan(ARRAY_CLOSE)
331
- break
332
- when skip(IGNORE)
333
- ;
334
- else
335
- raise ParserError, "unexpected token in array at '#{peek(20)}'!"
336
- end
337
- end
338
- result
339
- end
340
-
341
- def parse_object
342
- result = {}
343
- until eos?
344
- case
345
- when (string = parse_string) != UNPARSED
346
- skip(IGNORE)
347
- unless scan(PAIR_DELIMITER)
348
- raise ParserError, "expected ':' in object at '#{peek(20)}'!"
349
- end
350
- skip(IGNORE)
351
- unless (value = parse_value).equal? UNPARSED
352
- result[string] = value
353
- skip(IGNORE)
354
- unless scan(COLLECTION_DELIMITER) or match?(OBJECT_CLOSE)
355
- raise ParserError,
356
- "expected ',' or '}' in object at '#{peek(20)}'!"
357
- end
358
- else
359
- raise ParserError, "expected value in object at '#{peek(20)}'!"
360
- end
361
- when scan(OBJECT_CLOSE)
362
- if klassname = result['json_class']
363
- klass = klassname.sub(/^:+/, '').split(/::/).inject(Object) do |p,k|
364
- p.const_get(k) rescue nil
365
- end
366
- break unless klass and klass.json_creatable?
367
- result = klass.json_create(result)
368
- end
369
- break
370
- when skip(IGNORE)
371
- ;
372
- else
373
- raise ParserError, "unexpected token in object at '#{peek(20)}'!"
374
- end
375
- end
376
- result
377
- end
378
- end
379
-
380
- # This class is used to create State instances, that are use to hold data
381
- # while unparsing a Ruby data structure into a JSON string.
382
- class State
383
- # Creates a State object from _opts_, which ought to be Hash to create a
384
- # new State instance configured by opts, something else to create an
385
- # unconfigured instance. If _opts_ is a State object, it is just returned.
386
- def self.from_state(opts)
387
- case opts
388
- when self
389
- opts
390
- when Hash
391
- new(opts)
392
- else
393
- new
394
- end
395
- end
396
-
397
- # Instantiates a new State object, configured by _opts_.
398
- def initialize(opts = {})
399
- @indent = opts[:indent] || ''
400
- @space = opts[:space] || ''
401
- @object_nl = opts[:object_nl] || ''
402
- @array_nl = opts[:array_nl] || ''
403
- @seen = {}
404
- end
405
-
406
- # This string is used to indent levels in the JSON string.
407
- attr_accessor :indent
408
-
409
- # This string is used to include a space between the tokens in a JSON
410
- # string.
411
- attr_accessor :space
412
-
413
- # This string is put at the end of a line that holds a JSON object (or
414
- # Hash).
415
- attr_accessor :object_nl
416
-
417
- # This string is put at the end of a line that holds a JSON array.
418
- attr_accessor :array_nl
419
-
420
- # Returns _true_, if _object_ was already seen during this Unparsing run.
421
- def seen?(object)
422
- @seen.key?(object.__id__)
423
- end
424
-
425
- # Remember _object_, to find out if it was already encountered (to find out
426
- # if a cyclic data structure is unparsed).
427
- def remember(object)
428
- @seen[object.__id__] = true
429
- end
430
-
431
- # Forget _object_ for this Unparsing run.
432
- def forget(object)
433
- @seen.delete object.__id__
434
- end
435
- end
436
-
437
- module_function
438
-
439
- # Convert _string_ from UTF8 encoding to UTF16 (big endian) encoding and
440
- # return it.
441
- def utf8_to_utf16(string)
442
- JSON::UTF8toUTF16.iconv(string).unpack('H*')[0]
443
- end
444
-
445
- # Convert _string_ from UTF16 (big endian) encoding to UTF8 encoding and
446
- # return it.
447
- def utf16_to_utf8(string)
448
- bytes = '' << string[0, 2].to_i(16) << string[2, 2].to_i(16)
449
- JSON::UTF16toUTF8.iconv(bytes)
450
- end
451
-
452
- # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
453
- # UTF16 big endian characters as \u????, and return it.
454
- def utf8_to_json(string)
455
- i, n, result = 0, string.size, ''
456
- while i < n
457
- char = string[i]
458
- case
459
- when char == ?\b then result << '\b'
460
- when char == ?\t then result << '\t'
461
- when char == ?\n then result << '\n'
462
- when char == ?\f then result << '\f'
463
- when char == ?\r then result << '\r'
464
- when char == ?" then result << '\"'
465
- when char == ?\\ then result << '\\\\'
466
- when char == ?/ then result << '\/'
467
- when char.between?(0x0, 0x1f) then result << "\\u%04x" % char
468
- when char.between?(0x20, 0x7f) then result << char
469
- when !(JSON.support_unicode? && $KCODE == 'UTF8')
470
- # if utf8 mode is switched off or unicode not supported, just pass
471
- # bytes through:
472
- result << char
473
- when char & 0xe0 == 0xc0
474
- result << '\u' << utf8_to_utf16(string[i, 2])
475
- i += 1
476
- when char & 0xf0 == 0xe0
477
- result << '\u' << utf8_to_utf16(string[i, 3])
478
- i += 2
479
- when char & 0xf8 == 0xf0
480
- result << '\u' << utf8_to_utf16(string[i, 4])
481
- i += 3
482
- when char & 0xfc == 0xf8
483
- result << '\u' << utf8_to_utf16(string[i, 5])
484
- i += 4
485
- when char & 0xfe == 0xfc
486
- result << '\u' << utf8_to_utf16(string[i, 6])
487
- i += 5
488
- else
489
- raise JSON::UnparserError, "Encountered unknown UTF-8 byte: %x!" % char
490
- end
491
- i += 1
492
- end
493
- result
494
- end
495
-
496
- # Parse the JSON string _source_ into a Ruby data structure and return it.
497
- def parse(source)
498
- Parser.new(source).parse
499
- end
500
-
501
- # Unparse the Ruby data structure _obj_ into a single line JSON string and
502
- # return it. _state_ is a JSON::State object, that can be used to configure
503
- # the output further.
504
- def unparse(obj, state = nil)
505
- obj.to_json(JSON::State.from_state(state))
506
- end
507
-
508
- # Unparse the Ruby data structure _obj_ into a JSON string and return it.
509
- # The returned string is a prettier form of the string returned by #unparse.
510
- def pretty_unparse(obj)
511
- state = JSON::State.new(
512
- :indent => ' ',
513
- :space => ' ',
514
- :object_nl => "\n",
515
- :array_nl => "\n"
516
- )
517
- obj.to_json(state)
518
- end
519
- end
520
-
521
- class Object
522
- # Converts this object to a string (calling #to_s), converts
523
- # it to a JSON string, and returns the result. This is a fallback, if no
524
- # special method #to_json was defined for some object.
525
- # _state_ is a JSON::State object, that can also be used
526
- # to configure the produced JSON string output further.
527
-
528
- def to_json(*) to_s.to_json end
529
- end
530
-
531
- class Hash
532
- # Returns a JSON string containing a JSON object, that is unparsed from
533
- # this Hash instance.
534
- # _state_ is a JSON::State object, that can also be used to configure the
535
- # produced JSON string output further.
536
- # _depth_ is used to find out nesting depth, to indent accordingly.
537
- def to_json(state = nil, depth = 0)
538
- state = JSON::State.from_state(state)
539
- json_check_circular(state) { json_transform(state, depth) }
540
- end
541
-
542
- private
543
-
544
- def json_check_circular(state)
545
- if state
546
- state.seen?(self) and raise JSON::CircularDatastructure,
547
- "circular data structures not supported!"
548
- state.remember self
549
- end
550
- yield
551
- ensure
552
- state and state.forget self
553
- end
554
-
555
- def json_shift(state, depth)
556
- state and not state.object_nl.empty? or return ''
557
- state.indent * depth
558
- end
559
-
560
- def json_transform(state, depth)
561
- delim = ','
562
- delim << state.object_nl if state
563
- result = '{'
564
- result << state.object_nl if state
565
- result << map { |key,value|
566
- json_shift(state, depth + 1) <<
567
- key.to_s.to_json(state, depth + 1) <<
568
- ':' << state.space << value.to_json(state, depth + 1)
569
- }.join(delim)
570
- result << state.object_nl if state
571
- result << json_shift(state, depth)
572
- result << '}'
573
- result
574
- end
575
- end
576
-
577
- class Array
578
- # Returns a JSON string containing a JSON array, that is unparsed from
579
- # this Array instance.
580
- # _state_ is a JSON::State object, that can also be used to configure the
581
- # produced JSON string output further.
582
- # _depth_ is used to find out nesting depth, to indent accordingly.
583
- def to_json(state = nil, depth = 0)
584
- state = JSON::State.from_state(state)
585
- json_check_circular(state) { json_transform(state, depth) }
586
- end
587
-
588
- private
589
-
590
- def json_check_circular(state)
591
- if state
592
- state.seen?(self) and raise JSON::CircularDatastructure,
593
- "circular data structures not supported!"
594
- state.remember self
595
- end
596
- yield
597
- ensure
598
- state and state.forget self
599
- end
600
-
601
- def json_shift(state, depth)
602
- state and not state.array_nl.empty? or return ''
603
- state.indent * depth
604
- end
605
-
606
- def json_transform(state, depth)
607
- delim = ','
608
- delim << state.array_nl if state
609
- result = '['
610
- result << state.array_nl if state
611
- result << map { |value|
612
- json_shift(state, depth + 1) << value.to_json(state, depth + 1)
613
- }.join(delim)
614
- result << state.array_nl if state
615
- result << json_shift(state, depth)
616
- result << ']'
617
- result
618
- end
619
- end
620
-
621
- class Integer
622
- # Returns a JSON string representation for this Integer number.
623
- def to_json(*) to_s end
624
- end
625
-
626
- class Float
627
- # Returns a JSON string representation for this Float number.
628
- def to_json(*) to_s end
629
- end
630
-
631
- class String
632
- # This string should be encoded with UTF-8 (if JSON unicode support is
633
- # enabled). A call to this method returns a JSON string
634
- # encoded with UTF16 big endian characters as \u????. If
635
- # JSON.support_unicode? is false only control characters are encoded this
636
- # way, all 8-bit bytes are just passed through.
637
- def to_json(*)
638
- '"' << JSON::utf8_to_json(self) << '"'
639
- end
640
-
641
- # Raw Strings are JSON Objects (the raw bytes are stored in an array for the
642
- # key "raw"). The Ruby String can be created by this class method.
643
- def self.json_create(o)
644
- o['raw'].pack('C*')
645
- end
646
-
647
- # This method creates a raw object, that can be nested into other data
648
- # structures and will be unparsed as a raw string.
649
- def to_json_raw_object
650
- {
651
- 'json_class' => self.class.name,
652
- 'raw' => self.unpack('C*'),
653
- }
654
- end
655
-
656
- # This method should be used, if you want to convert raw strings to JSON
657
- # instead of UTF-8 strings, e. g. binary data (and JSON Unicode support is
658
- # enabled).
659
- def to_json_raw(*args)
660
- to_json_raw_object.to_json(*args)
661
- end
662
- end
663
-
664
- class TrueClass
665
- # Returns a JSON string for true: 'true'.
666
- def to_json(*) to_s end
667
- end
668
-
669
- class FalseClass
670
- # Returns a JSON string for false: 'false'.
671
- def to_json(*) to_s end
672
- end
673
-
674
- class NilClass
675
- # Returns a JSON string for nil: 'null'.
676
- def to_json(*) 'null' end
677
- end
678
-
679
- module Kernel
680
- # Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
681
- # one line.
682
- def j(*objs)
683
- objs.each do |obj|
684
- puts JSON::unparse(obj)
685
- end
686
- nil
687
- end
688
-
689
- # Ouputs _objs_ to STDOUT as JSON strings in a pretty format, with
690
- # indentation and over many lines.
691
- def jj(*objs)
692
- objs.each do |obj|
693
- puts JSON::pretty_unparse(obj)
694
- end
695
- nil
696
- end
697
- end
698
-
699
- class Class
700
- # Returns true, if this class can be used to create an instance
701
- # from a serialised JSON string. The class has to implement a class
702
- # method _json_create_ that expects a hash as first parameter, which includes
703
- # the required data.
704
- def json_creatable?
705
- respond_to?(:json_create)
706
- end
707
- end
708
- # vim: set et sw=2 ts=2: