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
@@ -0,0 +1,184 @@
1
+ require 'json/version'
2
+
3
+ module JSON
4
+ class << self
5
+ # If object is string like parse the string and return the parsed result as a
6
+ # Ruby data structure. Otherwise generate a JSON text from the Ruby data
7
+ # structure object and return it.
8
+ def [](object)
9
+ if object.respond_to? :to_str
10
+ JSON.parse(object.to_str)
11
+ else
12
+ JSON.generate(object)
13
+ end
14
+ end
15
+
16
+ # Returns the JSON parser class, that is used by JSON. This might be either
17
+ # JSON::Ext::Parser or JSON::Pure::Parser.
18
+ attr_reader :parser
19
+
20
+ # Set the JSON parser class _parser_ to be used by JSON.
21
+ def parser=(parser) # :nodoc:
22
+ @parser = parser
23
+ remove_const :Parser if const_defined? :Parser
24
+ const_set :Parser, parser
25
+ end
26
+
27
+ # Return the constant located at _path_. The format of _path_ has to be
28
+ # either ::A::B::C or A::B::C. In any case A has to be located at the top
29
+ # level (absolute namespace path?). If there doesn't exist a constant at
30
+ # the given path, an ArgumentError is raised.
31
+ def deep_const_get(path) # :nodoc:
32
+ path.split(/::/).inject(Object) do |p, c|
33
+ case
34
+ when c.empty? then p
35
+ when p.const_defined?(c) then p.const_get(c)
36
+ else raise ArgumentError, "can't find #{path}"
37
+ end
38
+ end
39
+ end
40
+
41
+ # Set the module _generator_ to be used by JSON.
42
+ def generator=(generator) # :nodoc:
43
+ @generator = generator
44
+ generator_methods = generator::GeneratorMethods
45
+ for const in generator_methods.constants
46
+ klass = deep_const_get(const)
47
+ modul = generator_methods.const_get(const)
48
+ klass.class_eval do
49
+ instance_methods(false).each do |m|
50
+ m.to_s == 'to_json' and remove_method m
51
+ end
52
+ include modul
53
+ end
54
+ end
55
+ self.state = generator::State
56
+ const_set :State, self.state
57
+ end
58
+
59
+ # Returns the JSON generator modul, that is used by JSON. This might be
60
+ # either JSON::Ext::Generator or JSON::Pure::Generator.
61
+ attr_reader :generator
62
+
63
+ # Returns the JSON generator state class, that is used by JSON. This might
64
+ # be either JSON::Ext::Generator::State or JSON::Pure::Generator::State.
65
+ attr_accessor :state
66
+
67
+ # This is create identifier, that is used to decide, if the _json_create_
68
+ # hook of a class should be called. It defaults to 'json_class'.
69
+ attr_accessor :create_id
70
+ end
71
+ self.create_id = 'json_class'
72
+
73
+ # The base exception for JSON errors.
74
+ class JSONError < StandardError; end
75
+
76
+ # This exception is raised, if a parser error occurs.
77
+ class ParserError < JSONError; end
78
+
79
+ # This exception is raised, if a generator or unparser error occurs.
80
+ class GeneratorError < JSONError; end
81
+ # For backwards compatibility
82
+ UnparserError = GeneratorError
83
+
84
+ # If a circular data structure is encountered while unparsing
85
+ # this exception is raised.
86
+ class CircularDatastructure < GeneratorError; end
87
+
88
+ # This exception is raised, if the required unicode support is missing on the
89
+ # system. Usually this means, that the iconv library is not installed.
90
+ class MissingUnicodeSupport < JSONError; end
91
+
92
+ module_function
93
+
94
+ # Parse the JSON string _source_ into a Ruby data structure and return it.
95
+ def parse(source)
96
+ JSON.parser.new(source).parse
97
+ end
98
+
99
+ # Unparse the Ruby data structure _obj_ into a single line JSON string and
100
+ # return it. _state_ is a JSON::State object, that can be used to configure
101
+ # the output further.
102
+ #
103
+ # It defaults to a state object, that creates the shortest possible JSON text
104
+ # in one line and only checks for circular data structures. If you are sure,
105
+ # that the objects don't contain any circles, you can set _state_ to nil, to
106
+ # disable these checks in order to create the JSON text faster. See also
107
+ # fast_generate.
108
+ def generate(obj, state = JSON.state.new)
109
+ obj.to_json(state)
110
+ end
111
+
112
+ alias unparse generate
113
+ module_function :unparse
114
+
115
+ # Unparse the Ruby data structure _obj_ into a single line JSON string and
116
+ # return it. This method disables the checks for circles in Ruby objects.
117
+ #
118
+ # *WARNING*: Be careful not to pass any Ruby data structures with circles as
119
+ # _obj_ argument, because this will cause JSON to go into an infinite loop.
120
+ def fast_generate(obj)
121
+ obj.to_json(nil)
122
+ end
123
+
124
+ alias fast_unparse fast_generate
125
+ module_function :fast_unparse
126
+
127
+ # Unparse the Ruby data structure _obj_ into a JSON string and return it. The
128
+ # returned string is a prettier form of the string returned by #unparse.
129
+ def pretty_generate(obj)
130
+ state = JSON.state.new(
131
+ :indent => ' ',
132
+ :space => ' ',
133
+ :object_nl => "\n",
134
+ :array_nl => "\n",
135
+ :check_circular => true
136
+ )
137
+ obj.to_json(state)
138
+ end
139
+
140
+ alias pretty_unparse pretty_generate
141
+ module_function :pretty_unparse
142
+ end
143
+
144
+ module ::Kernel
145
+ # Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
146
+ # one line.
147
+ def j(*objs)
148
+ objs.each do |obj|
149
+ puts JSON::generate(obj)
150
+ end
151
+ nil
152
+ end
153
+
154
+ # Ouputs _objs_ to STDOUT as JSON strings in a pretty format, with
155
+ # indentation and over many lines.
156
+ def jj(*objs)
157
+ objs.each do |obj|
158
+ puts JSON::pretty_generate(obj)
159
+ end
160
+ nil
161
+ end
162
+
163
+ # If object is string like parse the string and return the parsed result as a
164
+ # Ruby data structure. Otherwise generate a JSON text from the Ruby data
165
+ # structure object and return it.
166
+ def JSON(object)
167
+ if object.respond_to? :to_str
168
+ JSON.parse(object.to_str)
169
+ else
170
+ JSON.generate(object)
171
+ end
172
+ end
173
+ end
174
+
175
+ class ::Class
176
+ # Returns true, if this class can be used to create an instance
177
+ # from a serialised JSON string. The class has to implement a class
178
+ # method _json_create_ that expects a hash as first parameter, which includes
179
+ # the required data.
180
+ def json_creatable?
181
+ respond_to?(:json_create)
182
+ end
183
+ end
184
+ # vim: set et sw=2 ts=2:
@@ -42,6 +42,11 @@ module JSON
42
42
  MessageDialog::ERROR,
43
43
  MessageDialog::BUTTONS_CLOSE, text)
44
44
  dialog.run
45
+ rescue TypeError
46
+ dialog = MessageDialog.new(Editor.window, Dialog::MODAL,
47
+ MessageDialog::ERROR,
48
+ MessageDialog::BUTTONS_CLOSE, text)
49
+ dialog.run
45
50
  ensure
46
51
  dialog.destroy if dialog
47
52
  end
@@ -1182,16 +1187,20 @@ module JSON
1182
1187
  end
1183
1188
  end
1184
1189
 
1185
- # Starts a JSON Editor. If a block was given, it yields
1186
- # to the JSON::Editor::MainWindow instance.
1187
- def Editor.start(encoding = nil) # :yield: window
1188
- encoding ||= 'utf8'
1189
- Gtk.init
1190
- window = Editor::MainWindow.new(encoding)
1191
- window.icon_list = [ Editor.fetch_icon('json') ]
1192
- yield window if block_given?
1193
- window.show_all
1194
- Gtk.main
1190
+ class << self
1191
+ # Starts a JSON Editor. If a block was given, it yields
1192
+ # to the JSON::Editor::MainWindow instance.
1193
+ def start(encoding = nil) # :yield: window
1194
+ encoding ||= 'utf8'
1195
+ Gtk.init
1196
+ @window = Editor::MainWindow.new(encoding)
1197
+ @window.icon_list = [ Editor.fetch_icon('json') ]
1198
+ yield @window if block_given?
1199
+ @window.show_all
1200
+ Gtk.main
1201
+ end
1202
+
1203
+ attr_reader :window
1195
1204
  end
1196
1205
  end
1197
1206
  end
@@ -0,0 +1,13 @@
1
+ require 'json/common'
2
+
3
+ module JSON
4
+ # This module holds all the modules/classes that implement JSON's
5
+ # functionality as C extensions.
6
+ module Ext
7
+ require 'json/ext/parser'
8
+ require 'json/ext/generator'
9
+ $DEBUG and warn "Using c extension for JSON."
10
+ JSON.parser = Parser
11
+ JSON.generator = Generator
12
+ end
13
+ end
@@ -0,0 +1,75 @@
1
+ require 'json/common'
2
+ require 'json/pure/parser'
3
+ require 'json/pure/generator'
4
+
5
+ module JSON
6
+ begin
7
+ require 'iconv'
8
+ # An iconv instance to convert from UTF8 to UTF16 Big Endian.
9
+ UTF16toUTF8 = Iconv.new('utf-8', 'utf-16be') # :nodoc:
10
+ # An iconv instance to convert from UTF16 Big Endian to UTF8.
11
+ UTF8toUTF16 = Iconv.new('utf-16be', 'utf-8') # :nodoc:
12
+ UTF8toUTF16.iconv('no bom')
13
+ rescue Errno::EINVAL, Iconv::InvalidEncoding
14
+ # Iconv doesn't support big endian utf-16. Let's try to hack this manually
15
+ # into the converters.
16
+ begin
17
+ old_verbose, $VERBSOSE = $VERBOSE, nil
18
+ # An iconv instance to convert from UTF8 to UTF16 Big Endian.
19
+ UTF16toUTF8 = Iconv.new('utf-8', 'utf-16') # :nodoc:
20
+ # An iconv instance to convert from UTF16 Big Endian to UTF8.
21
+ UTF8toUTF16 = Iconv.new('utf-16', 'utf-8') # :nodoc:
22
+ UTF8toUTF16.iconv('no bom')
23
+ if UTF8toUTF16.iconv("\xe2\x82\xac") == "\xac\x20"
24
+ swapper = Class.new do
25
+ def initialize(iconv) # :nodoc:
26
+ @iconv = iconv
27
+ end
28
+
29
+ def iconv(string) # :nodoc:
30
+ result = @iconv.iconv(string)
31
+ JSON.swap!(result)
32
+ end
33
+ end
34
+ UTF8toUTF16 = swapper.new(UTF8toUTF16) # :nodoc:
35
+ end
36
+ if UTF16toUTF8.iconv("\xac\x20") == "\xe2\x82\xac"
37
+ swapper = Class.new do
38
+ def initialize(iconv) # :nodoc:
39
+ @iconv = iconv
40
+ end
41
+
42
+ def iconv(string) # :nodoc:
43
+ string = JSON.swap!(string.dup)
44
+ @iconv.iconv(string)
45
+ end
46
+ end
47
+ UTF16toUTF8 = swapper.new(UTF16toUTF8) # :nodoc:
48
+ end
49
+ rescue Errno::EINVAL, Iconv::InvalidEncoding
50
+ raise MissingUnicodeSupport, "iconv doesn't seem to support UTF-8/UTF-16 conversions"
51
+ ensure
52
+ $VERBOSE = old_verbose
53
+ end
54
+ rescue LoadError
55
+ raise MissingUnicodeSupport,
56
+ "iconv couldn't be loaded, which is required for UTF-8/UTF-16 conversions"
57
+ end
58
+
59
+ # Swap consecutive bytes of _string_ in place.
60
+ def self.swap!(string) # :nodoc:
61
+ 0.upto(string.size / 2) do |i|
62
+ break unless string[2 * i + 1]
63
+ string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
64
+ end
65
+ string
66
+ end
67
+
68
+ # This module holds all the modules/classes that implement JSON's
69
+ # functionality in pure ruby.
70
+ module Pure
71
+ $DEBUG and warn "Using pure library for JSON."
72
+ JSON.parser = Parser
73
+ JSON.generator = Generator
74
+ end
75
+ end
@@ -0,0 +1,321 @@
1
+ module JSON
2
+ MAP = {
3
+ "\x0" => '\u0000',
4
+ "\x1" => '\u0001',
5
+ "\x2" => '\u0002',
6
+ "\x3" => '\u0003',
7
+ "\x4" => '\u0004',
8
+ "\x5" => '\u0005',
9
+ "\x6" => '\u0006',
10
+ "\x7" => '\u0007',
11
+ "\b" => '\b',
12
+ "\t" => '\t',
13
+ "\n" => '\n',
14
+ "\xb" => '\u000b',
15
+ "\f" => '\f',
16
+ "\r" => '\r',
17
+ "\xe" => '\u000e',
18
+ "\xf" => '\u000f',
19
+ "\x10" => '\u0010',
20
+ "\x11" => '\u0011',
21
+ "\x12" => '\u0012',
22
+ "\x13" => '\u0013',
23
+ "\x14" => '\u0014',
24
+ "\x15" => '\u0015',
25
+ "\x16" => '\u0016',
26
+ "\x17" => '\u0017',
27
+ "\x18" => '\u0018',
28
+ "\x19" => '\u0019',
29
+ "\x1a" => '\u001a',
30
+ "\x1b" => '\u001b',
31
+ "\x1c" => '\u001c',
32
+ "\x1d" => '\u001d',
33
+ "\x1e" => '\u001e',
34
+ "\x1f" => '\u001f',
35
+ '"' => '\"',
36
+ '\\' => '\\\\',
37
+ '/' => '\/',
38
+ } # :nodoc:
39
+
40
+ # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
41
+ # UTF16 big endian characters as \u????, and return it.
42
+ def utf8_to_json(string) # :nodoc:
43
+ string = string.gsub(/["\\\/\x0-\x1f]/) { |c| MAP[c] }
44
+ string.gsub!(/(
45
+ (?:
46
+ [\xc2-\xdf][\x80-\xbf] |
47
+ [\xe0-\xef][\x80-\xbf]{2} |
48
+ [\xf0-\xf4][\x80-\xbf]{3}
49
+ )+ |
50
+ [\x80-\xc1\xf5-\xff] # invalid
51
+ )/nx) { |c|
52
+ c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
53
+ s = JSON::UTF8toUTF16.iconv(c).unpack('H*')[0]
54
+ s.gsub!(/.{4}/n, '\\\\u\&')
55
+ }
56
+ string
57
+ rescue Iconv::Failure => e
58
+ raise GeneratorError, "Caught #{e.class}: #{e}"
59
+ end
60
+ module_function :utf8_to_json
61
+
62
+ module Pure
63
+ module Generator
64
+ # This class is used to create State instances, that are use to hold data
65
+ # while generating a JSON text from a a Ruby data structure.
66
+ class State
67
+ # Creates a State object from _opts_, which ought to be Hash to create
68
+ # a new State instance configured by _opts_, something else to create
69
+ # an unconfigured instance. If _opts_ is a State object, it is just
70
+ # returned.
71
+ def self.from_state(opts)
72
+ case opts
73
+ when self
74
+ opts
75
+ when Hash
76
+ new(opts)
77
+ else
78
+ new
79
+ end
80
+ end
81
+
82
+ # Instantiates a new State object, configured by _opts_.
83
+ #
84
+ # _opts_ can have the following keys:
85
+ #
86
+ # * *indent*: a string used to indent levels (default: ''),
87
+ # * *space*: a string that is put after, a : or , delimiter (default: ''),
88
+ # * *space_before*: a string that is put before a : pair delimiter (default: ''),
89
+ # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
90
+ # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
91
+ # * *check_circular*: true if checking for circular data structures
92
+ # should be done, false (the default) otherwise.
93
+ def initialize(opts = {})
94
+ @indent = opts[:indent] || ''
95
+ @space = opts[:space] || ''
96
+ @space_before = opts[:space_before] || ''
97
+ @object_nl = opts[:object_nl] || ''
98
+ @array_nl = opts[:array_nl] || ''
99
+ @check_circular = !!(opts[:check_circular] || false)
100
+ @seen = {}
101
+ end
102
+
103
+ # This string is used to indent levels in the JSON text.
104
+ attr_accessor :indent
105
+
106
+ # This string is used to insert a space between the tokens in a JSON
107
+ # string.
108
+ attr_accessor :space
109
+
110
+ # This string is used to insert a space before the ':' in JSON objects.
111
+ attr_accessor :space_before
112
+
113
+ # This string is put at the end of a line that holds a JSON object (or
114
+ # Hash).
115
+ attr_accessor :object_nl
116
+
117
+ # This string is put at the end of a line that holds a JSON array.
118
+ attr_accessor :array_nl
119
+
120
+ # Returns true, if circular data structures should be checked,
121
+ # otherwise returns false.
122
+ def check_circular?
123
+ @check_circular
124
+ end
125
+
126
+ # Returns _true_, if _object_ was already seen during this generating
127
+ # run.
128
+ def seen?(object)
129
+ @seen.key?(object.__id__)
130
+ end
131
+
132
+ # Remember _object_, to find out if it was already encountered (if a
133
+ # cyclic data structure is if a cyclic data structure is rendered).
134
+ def remember(object)
135
+ @seen[object.__id__] = true
136
+ end
137
+
138
+ # Forget _object_ for this generating run.
139
+ def forget(object)
140
+ @seen.delete object.__id__
141
+ end
142
+ end
143
+
144
+ module GeneratorMethods
145
+ module Object
146
+ # Converts this object to a string (calling #to_s), converts
147
+ # it to a JSON string, and returns the result. This is a fallback, if no
148
+ # special method #to_json was defined for some object.
149
+ def to_json(*) to_s.to_json end
150
+ end
151
+
152
+ module Hash
153
+ # Returns a JSON string containing a JSON object, that is unparsed from
154
+ # this Hash instance.
155
+ # _state_ is a JSON::State object, that can also be used to configure the
156
+ # produced JSON string output further.
157
+ # _depth_ is used to find out nesting depth, to indent accordingly.
158
+ def to_json(state = nil, depth = 0, *)
159
+ if state
160
+ state = JSON.state.from_state(state)
161
+ json_check_circular(state) { json_transform(state, depth) }
162
+ else
163
+ json_transform(state, depth)
164
+ end
165
+ end
166
+
167
+ private
168
+
169
+ def json_check_circular(state)
170
+ if state
171
+ state.seen?(self) and raise JSON::CircularDatastructure,
172
+ "circular data structures not supported!"
173
+ state.remember self
174
+ end
175
+ yield
176
+ ensure
177
+ state and state.forget self
178
+ end
179
+
180
+ def json_shift(state, depth)
181
+ state and not state.object_nl.empty? or return ''
182
+ state.indent * depth
183
+ end
184
+
185
+ def json_transform(state, depth)
186
+ delim = ','
187
+ delim << state.object_nl if state
188
+ result = '{'
189
+ result << state.object_nl if state
190
+ result << map { |key,value|
191
+ s = json_shift(state, depth + 1)
192
+ s << key.to_s.to_json(state, depth + 1)
193
+ s << state.space_before if state
194
+ s << ':'
195
+ s << state.space if state
196
+ s << value.to_json(state, depth + 1)
197
+ }.join(delim)
198
+ result << state.object_nl if state
199
+ result << json_shift(state, depth)
200
+ result << '}'
201
+ result
202
+ end
203
+ end
204
+
205
+ module Array
206
+ # Returns a JSON string containing a JSON array, that is unparsed from
207
+ # this Array instance.
208
+ # _state_ is a JSON::State object, that can also be used to configure the
209
+ # produced JSON string output further.
210
+ # _depth_ is used to find out nesting depth, to indent accordingly.
211
+ def to_json(state = nil, depth = 0, *)
212
+ if state
213
+ state = JSON.state.from_state(state)
214
+ json_check_circular(state) { json_transform(state, depth) }
215
+ else
216
+ json_transform(state, depth)
217
+ end
218
+ end
219
+
220
+ private
221
+
222
+ def json_check_circular(state)
223
+ if state
224
+ state.seen?(self) and raise JSON::CircularDatastructure,
225
+ "circular data structures not supported!"
226
+ state.remember self
227
+ end
228
+ yield
229
+ ensure
230
+ state and state.forget self
231
+ end
232
+
233
+ def json_shift(state, depth)
234
+ state and not state.array_nl.empty? or return ''
235
+ state.indent * depth
236
+ end
237
+
238
+ def json_transform(state, depth)
239
+ delim = ','
240
+ delim << state.array_nl if state
241
+ result = '['
242
+ result << state.array_nl if state
243
+ result << map { |value|
244
+ json_shift(state, depth + 1) << value.to_json(state, depth + 1)
245
+ }.join(delim)
246
+ result << state.array_nl if state
247
+ result << json_shift(state, depth)
248
+ result << ']'
249
+ result
250
+ end
251
+ end
252
+
253
+ module Integer
254
+ # Returns a JSON string representation for this Integer number.
255
+ def to_json(*) to_s end
256
+ end
257
+
258
+ module Float
259
+ # Returns a JSON string representation for this Float number.
260
+ def to_json(*) to_s end
261
+ end
262
+
263
+ module String
264
+ # This string should be encoded with UTF-8 A call to this method
265
+ # returns a JSON string encoded with UTF16 big endian characters as
266
+ # \u????.
267
+ def to_json(*)
268
+ '"' << JSON.utf8_to_json(self) << '"'
269
+ end
270
+
271
+ # Module that holds the extinding methods if, the String module is
272
+ # included.
273
+ module Extend
274
+ # Raw Strings are JSON Objects (the raw bytes are stored in an array for the
275
+ # key "raw"). The Ruby String can be created by this module method.
276
+ def json_create(o)
277
+ o['raw'].pack('C*')
278
+ end
279
+ end
280
+
281
+ # Extends _modul_ with the String::Extend module.
282
+ def self.included(modul)
283
+ modul.extend Extend
284
+ end
285
+
286
+ # This method creates a raw object hash, that can be nested into
287
+ # other data structures and will be unparsed as a raw string. This
288
+ # method should be used, if you want to convert raw strings to JSON
289
+ # instead of UTF-8 strings, e. g. binary data.
290
+ def to_json_raw_object
291
+ {
292
+ JSON.create_id => self.class.name,
293
+ 'raw' => self.unpack('C*'),
294
+ }
295
+ end
296
+
297
+ # This method creates a JSON text from the result of
298
+ # a call to to_json_raw_object of this String.
299
+ def to_json_raw(*args)
300
+ to_json_raw_object.to_json(*args)
301
+ end
302
+ end
303
+
304
+ module TrueClass
305
+ # Returns a JSON string for true: 'true'.
306
+ def to_json(*) 'true' end
307
+ end
308
+
309
+ module FalseClass
310
+ # Returns a JSON string for false: 'false'.
311
+ def to_json(*) 'false' end
312
+ end
313
+
314
+ module NilClass
315
+ # Returns a JSON string for nil: 'null'.
316
+ def to_json(*) 'null' end
317
+ end
318
+ end
319
+ end
320
+ end
321
+ end