oj 2.0.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +17 -23
- data/README.md +74 -425
- data/ext/oj/buf.h +103 -0
- data/ext/oj/cache8.c +4 -0
- data/ext/oj/circarray.c +68 -0
- data/ext/oj/circarray.h +23 -0
- data/ext/oj/code.c +227 -0
- data/ext/oj/code.h +40 -0
- data/ext/oj/compat.c +243 -0
- data/ext/oj/custom.c +1097 -0
- data/ext/oj/dump.c +766 -1534
- data/ext/oj/dump.h +92 -0
- data/ext/oj/dump_compat.c +937 -0
- data/ext/oj/dump_leaf.c +254 -0
- data/ext/oj/dump_object.c +810 -0
- data/ext/oj/dump_rails.c +329 -0
- data/ext/oj/dump_strict.c +416 -0
- data/ext/oj/encode.h +51 -0
- data/ext/oj/err.c +57 -0
- data/ext/oj/err.h +70 -0
- data/ext/oj/extconf.rb +17 -7
- data/ext/oj/fast.c +213 -180
- data/ext/oj/hash.c +163 -0
- data/ext/oj/hash.h +46 -0
- data/ext/oj/hash_test.c +512 -0
- data/ext/oj/mimic_json.c +817 -0
- data/ext/oj/mimic_rails.c +806 -0
- data/ext/oj/mimic_rails.h +17 -0
- data/ext/oj/object.c +752 -0
- data/ext/oj/odd.c +230 -0
- data/ext/oj/odd.h +44 -0
- data/ext/oj/oj.c +1288 -929
- data/ext/oj/oj.h +240 -69
- data/ext/oj/parse.c +1014 -0
- data/ext/oj/parse.h +92 -0
- data/ext/oj/reader.c +223 -0
- data/ext/oj/reader.h +151 -0
- data/ext/oj/resolve.c +127 -0
- data/ext/oj/{cache.h → resolve.h} +6 -13
- data/ext/oj/rxclass.c +133 -0
- data/ext/oj/rxclass.h +27 -0
- data/ext/oj/saj.c +77 -175
- data/ext/oj/scp.c +224 -0
- data/ext/oj/sparse.c +911 -0
- data/ext/oj/stream_writer.c +301 -0
- data/ext/oj/strict.c +162 -0
- data/ext/oj/string_writer.c +480 -0
- data/ext/oj/val_stack.c +98 -0
- data/ext/oj/val_stack.h +188 -0
- data/lib/oj/active_support_helper.rb +41 -0
- data/lib/oj/bag.rb +6 -10
- data/lib/oj/easy_hash.rb +52 -0
- data/lib/oj/json.rb +172 -0
- data/lib/oj/mimic.rb +260 -5
- data/lib/oj/saj.rb +13 -10
- data/lib/oj/schandler.rb +142 -0
- data/lib/oj/state.rb +131 -0
- data/lib/oj/version.rb +1 -1
- data/lib/oj.rb +11 -23
- data/pages/Advanced.md +22 -0
- data/pages/Compatibility.md +25 -0
- data/pages/Custom.md +23 -0
- data/pages/Encoding.md +65 -0
- data/pages/JsonGem.md +79 -0
- data/pages/Modes.md +140 -0
- data/pages/Options.md +250 -0
- data/pages/Rails.md +60 -0
- data/pages/Security.md +20 -0
- data/test/_test_active.rb +76 -0
- data/test/_test_active_mimic.rb +96 -0
- data/test/_test_mimic_rails.rb +126 -0
- data/test/activesupport4/decoding_test.rb +105 -0
- data/test/activesupport4/encoding_test.rb +531 -0
- data/test/activesupport4/test_helper.rb +41 -0
- data/test/activesupport5/decoding_test.rb +125 -0
- data/test/activesupport5/encoding_test.rb +483 -0
- data/test/activesupport5/encoding_test_cases.rb +90 -0
- data/test/activesupport5/test_helper.rb +50 -0
- data/test/activesupport5/time_zone_test_helpers.rb +24 -0
- data/test/helper.rb +27 -0
- data/test/isolated/shared.rb +310 -0
- data/test/isolated/test_mimic_after.rb +13 -0
- data/test/isolated/test_mimic_alone.rb +12 -0
- data/test/isolated/test_mimic_as_json.rb +45 -0
- data/test/isolated/test_mimic_before.rb +13 -0
- data/test/isolated/test_mimic_define.rb +28 -0
- data/test/isolated/test_mimic_rails_after.rb +22 -0
- data/test/isolated/test_mimic_rails_before.rb +21 -0
- data/test/isolated/test_mimic_redefine.rb +15 -0
- data/test/json_gem/json_addition_test.rb +216 -0
- data/test/json_gem/json_common_interface_test.rb +143 -0
- data/test/json_gem/json_encoding_test.rb +109 -0
- data/test/json_gem/json_ext_parser_test.rb +20 -0
- data/test/json_gem/json_fixtures_test.rb +35 -0
- data/test/json_gem/json_generator_test.rb +383 -0
- data/test/json_gem/json_generic_object_test.rb +90 -0
- data/test/json_gem/json_parser_test.rb +470 -0
- data/test/json_gem/json_string_matching_test.rb +42 -0
- data/test/json_gem/test_helper.rb +18 -0
- data/test/perf_compat.rb +130 -0
- data/test/perf_fast.rb +9 -9
- data/test/perf_file.rb +64 -0
- data/test/{perf_obj.rb → perf_object.rb} +24 -10
- data/test/perf_scp.rb +151 -0
- data/test/perf_strict.rb +32 -113
- data/test/sample.rb +2 -3
- data/test/test_compat.rb +474 -0
- data/test/test_custom.rb +355 -0
- data/test/test_debian.rb +53 -0
- data/test/test_fast.rb +66 -16
- data/test/test_file.rb +237 -0
- data/test/test_gc.rb +49 -0
- data/test/test_hash.rb +29 -0
- data/test/test_null.rb +376 -0
- data/test/test_object.rb +1010 -0
- data/test/test_saj.rb +16 -16
- data/test/test_scp.rb +417 -0
- data/test/test_strict.rb +410 -0
- data/test/test_various.rb +815 -0
- data/test/test_writer.rb +308 -0
- data/test/tests.rb +9 -902
- data/test/tests_mimic.rb +14 -0
- data/test/tests_mimic_addition.rb +7 -0
- metadata +253 -38
- data/ext/oj/cache.c +0 -148
- data/ext/oj/foo.rb +0 -6
- data/ext/oj/load.c +0 -1049
- data/test/a.rb +0 -38
- data/test/perf1.rb +0 -64
- data/test/perf2.rb +0 -76
- data/test/perf_obj_old.rb +0 -213
- data/test/test_mimic.rb +0 -208
data/lib/oj/mimic.rb
CHANGED
@@ -1,12 +1,267 @@
|
|
1
1
|
|
2
|
+
require 'bigdecimal'
|
3
|
+
begin
|
4
|
+
require 'ostruct'
|
5
|
+
rescue Exception
|
6
|
+
# ignore
|
7
|
+
end
|
8
|
+
|
2
9
|
module Oj
|
3
|
-
|
10
|
+
|
11
|
+
# A bit hack-ish but does the trick. The JSON.dump_default_options is a Hash
|
12
|
+
# but in mimic we use a C struct to store defaults. This class creates a view
|
13
|
+
# onto that struct.
|
14
|
+
class MimicDumpOption < Hash
|
15
|
+
def initialize()
|
16
|
+
oo = Oj.default_options
|
17
|
+
self.store(:max_nesting, false)
|
18
|
+
self.store(:allow_nan, true)
|
19
|
+
self.store(:quirks_mode, oo[:quirks_mode])
|
20
|
+
self.store(:ascii_only, (:ascii == oo[:escape_mode]))
|
21
|
+
end
|
22
|
+
|
23
|
+
def []=(key, value)
|
24
|
+
case key
|
25
|
+
when :quirks_mode
|
26
|
+
Oj.default_options = {:quirks_mode => value}
|
27
|
+
when :ascii_only
|
28
|
+
Oj.default_options = {:ascii_only => value}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Loads mimic-ed JSON paths. Used by Oj.mimic_JSON().
|
34
|
+
# @param mimic_path [Array] additional paths to add to the Ruby loaded features.
|
4
35
|
def self.mimic_loaded(mimic_paths=[])
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
36
|
+
$LOAD_PATH.each do |d|
|
37
|
+
next unless File.exist?(d)
|
38
|
+
|
39
|
+
jfile = File.join(d, 'json.rb')
|
40
|
+
$LOADED_FEATURES << jfile unless $LOADED_FEATURES.include?(jfile) if File.exist?(jfile)
|
41
|
+
|
42
|
+
Dir.glob(File.join(d, 'json', '**', '*.rb')).each do |file|
|
43
|
+
# allow json/add/xxx to be loaded. User can override with Oj.add_to_json(xxx).
|
44
|
+
$LOADED_FEATURES << file unless $LOADED_FEATURES.include?(file) unless file.include?('add')
|
45
|
+
end
|
9
46
|
end
|
10
47
|
mimic_paths.each { |p| $LOADED_FEATURES << p }
|
48
|
+
$LOADED_FEATURES << 'json' unless $LOADED_FEATURES.include?('json')
|
49
|
+
|
50
|
+
require 'oj/json'
|
51
|
+
|
52
|
+
if Object.const_defined?('OpenStruct')
|
53
|
+
OpenStruct.class_eval do
|
54
|
+
# Both the JSON gem and Rails monkey patch as_json. Let them battle it out.
|
55
|
+
unless defined?(self.as_json)
|
56
|
+
def as_json(*)
|
57
|
+
name = self.class.name.to_s
|
58
|
+
raise JSON::JSONError, "Only named structs are supported!" if 0 == name.length
|
59
|
+
{ JSON.create_id => name, 't' => table }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
def self.json_create(h)
|
63
|
+
new(h['t'] || h[:t])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
BigDecimal.class_eval do
|
69
|
+
# Both the JSON gem and Rails monkey patch as_json. Let them battle it out.
|
70
|
+
unless defined?(self.as_json)
|
71
|
+
def as_json(*)
|
72
|
+
{JSON.create_id => 'BigDecimal', 'b' => _dump }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
def self.json_create(h)
|
76
|
+
BigDecimal._load(h['b'])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
Complex.class_eval do
|
81
|
+
# Both the JSON gem and Rails monkey patch as_json. Let them battle it out.
|
82
|
+
unless defined?(self.as_json)
|
83
|
+
def as_json(*)
|
84
|
+
{JSON.create_id => 'Complex', 'r' => real, 'i' => imag }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
def self.json_create(h)
|
88
|
+
Complex(h['r'], h['i'])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
Date.class_eval do
|
93
|
+
# Both the JSON gem and Rails monkey patch as_json. Let them battle it out.
|
94
|
+
unless defined?(self.as_json)
|
95
|
+
def as_json(*)
|
96
|
+
{ JSON.create_id => 'Date', 'y' => year, 'm' => month, 'd' => day, 'sg' => start }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
def self.json_create(h)
|
100
|
+
civil(h['y'], h['m'], h['d'], h['sg'])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
DateTime.class_eval do
|
105
|
+
# Both the JSON gem and Rails monkey patch as_json. Let them battle it out.
|
106
|
+
unless defined?(self.as_json)
|
107
|
+
def as_json(*)
|
108
|
+
{ JSON.create_id => 'DateTime',
|
109
|
+
'y' => year,
|
110
|
+
'm' => month,
|
111
|
+
'd' => day,
|
112
|
+
'H' => hour,
|
113
|
+
'M' => min,
|
114
|
+
'S' => sec,
|
115
|
+
'of' => offset.to_s,
|
116
|
+
'sg' => start }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
def self.json_create(h)
|
120
|
+
# offset is a rational as a string
|
121
|
+
as, bs = h['of'].split('/')
|
122
|
+
a = as.to_i
|
123
|
+
b = bs.to_i
|
124
|
+
if 0 == b
|
125
|
+
off = a
|
126
|
+
else
|
127
|
+
off = Rational(a, b)
|
128
|
+
end
|
129
|
+
civil(h['y'], h['m'], h['d'], h['H'], h['M'], h['S'], off, h['sg'])
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
Date.class_eval do
|
134
|
+
# Both the JSON gem and Rails monkey patch as_json. Let them battle it out.
|
135
|
+
unless defined?(self.as_json)
|
136
|
+
def as_json(*)
|
137
|
+
{ JSON.create_id => 'Date', 'y' => year, 'm' => month, 'd' => day, 'sg' => start }
|
138
|
+
end
|
139
|
+
end
|
140
|
+
def self.json_create(h)
|
141
|
+
civil(h['y'], h['m'], h['d'], h['sg'])
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
Exception.class_eval do
|
146
|
+
# Both the JSON gem and Rails monkey patch as_json. Let them battle it out.
|
147
|
+
unless defined?(self.as_json)
|
148
|
+
def as_json(*)
|
149
|
+
{JSON.create_id => self.class.name, 'm' => message, 'b' => backtrace }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
def self.json_create(h)
|
153
|
+
e = new(h['m'])
|
154
|
+
e.set_backtrace(h['b'])
|
155
|
+
e
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
Range.class_eval do
|
160
|
+
# Both the JSON gem and Rails monkey patch as_json. Let them battle it out.
|
161
|
+
unless defined?(self.as_json)
|
162
|
+
def as_json(*)
|
163
|
+
{JSON.create_id => 'Range', 'a' => [first, last, exclude_end?]}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
def self.json_create(h)
|
167
|
+
new(*h['a'])
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
Rational.class_eval do
|
172
|
+
# Both the JSON gem and Rails monkey patch as_json. Let them battle it out.
|
173
|
+
unless defined?(self.as_json)
|
174
|
+
def as_json(*)
|
175
|
+
{JSON.create_id => 'Rational', 'n' => numerator, 'd' => denominator }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
def self.json_create(h)
|
179
|
+
Rational(h['n'], h['d'])
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
Regexp.class_eval do
|
184
|
+
# Both the JSON gem and Rails monkey patch as_json. Let them battle it out.
|
185
|
+
unless defined?(self.as_json)
|
186
|
+
def as_json(*)
|
187
|
+
{JSON.create_id => 'Regexp', 'o' => options, 's' => source }
|
188
|
+
end
|
189
|
+
end
|
190
|
+
def self.json_create(h)
|
191
|
+
new(h['s'], h['o'])
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
Struct.class_eval do
|
196
|
+
# Both the JSON gem and Rails monkey patch as_json. Let them battle it out.
|
197
|
+
unless defined?(self.as_json)
|
198
|
+
def as_json(*)
|
199
|
+
name = self.class.name.to_s
|
200
|
+
raise JSON::JSONError, "Only named structs are supported!" if 0 == name.length
|
201
|
+
{ JSON.create_id => name, 'v' => values }
|
202
|
+
end
|
203
|
+
end
|
204
|
+
def self.json_create(h)
|
205
|
+
new(*h['v'])
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
Symbol.class_eval do
|
210
|
+
# Both the JSON gem and Rails monkey patch as_json. Let them battle it out.
|
211
|
+
unless defined?(self.as_json)
|
212
|
+
def as_json(*)
|
213
|
+
{JSON.create_id => 'Symbol', 's' => to_s }
|
214
|
+
end
|
215
|
+
end
|
216
|
+
def self.json_create(h)
|
217
|
+
h['s'].to_sym
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
Time.class_eval do
|
222
|
+
# Both the JSON gem and Rails monkey patch as_json. Let them battle it out.
|
223
|
+
unless defined?(self.as_json)
|
224
|
+
def as_json(*)
|
225
|
+
nsecs = [ tv_usec * 1000 ]
|
226
|
+
nsecs << tv_nsec if respond_to?(:tv_nsec)
|
227
|
+
nsecs = nsecs.max
|
228
|
+
{ JSON.create_id => 'Time', 's' => tv_sec, 'n' => nsecs }
|
229
|
+
end
|
230
|
+
end
|
231
|
+
def self.json_create(h)
|
232
|
+
if usec = h.delete('u')
|
233
|
+
h['n'] = usec * 1000
|
234
|
+
end
|
235
|
+
if instance_methods.include?(:tv_nsec)
|
236
|
+
at(h['s'], Rational(h['n'], 1000))
|
237
|
+
else
|
238
|
+
at(h['s'], h['n'] / 1000)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
end # self.mimic_loaded
|
244
|
+
|
245
|
+
end # Oj
|
246
|
+
|
247
|
+
# More monkey patches.
|
248
|
+
class String
|
249
|
+
def to_json_raw_object
|
250
|
+
{
|
251
|
+
JSON.create_id => self.class.name,
|
252
|
+
'raw' => self.bytes
|
253
|
+
}
|
254
|
+
end
|
255
|
+
def to_json_raw(*)
|
256
|
+
to_json_raw_object().to_json()
|
257
|
+
end
|
258
|
+
def self.json_create(obj)
|
259
|
+
s = ''
|
260
|
+
s.encode!(Encoding::ASCII_8BIT) if s.respond_to?(:encode!)
|
261
|
+
raw = obj['raw']
|
262
|
+
if raw.is_a? Array
|
263
|
+
raw.each { |v| s << v }
|
264
|
+
end
|
265
|
+
s
|
11
266
|
end
|
12
267
|
end
|
data/lib/oj/saj.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
module Oj
|
2
|
-
# A SAX style parse handler for JSON hence the acronym SAJ for Simple API for
|
3
|
-
#
|
4
|
-
# called as the file
|
2
|
+
# A SAX style parse handler for JSON hence the acronym SAJ for Simple API for
|
3
|
+
# JSON. The Oj::Saj handler class should be subclassed and then used with the
|
4
|
+
# Oj::Saj key_parse() method. The Saj methods will then be called as the file
|
5
|
+
# is parsed.
|
6
|
+
#
|
5
7
|
# @example
|
6
8
|
#
|
7
9
|
# require 'oj'
|
@@ -11,18 +13,19 @@ module Oj
|
|
11
13
|
# @hash_cnt = 0
|
12
14
|
# end
|
13
15
|
#
|
14
|
-
# def
|
16
|
+
# def hash_start(key)
|
15
17
|
# @hash_cnt += 1
|
16
18
|
# end
|
17
19
|
# end
|
18
20
|
#
|
19
21
|
# cnt = MySaj.new()
|
20
|
-
# File.open('any.
|
22
|
+
# File.open('any.json', 'r') do |f|
|
21
23
|
# Oj.saj_parse(cnt, f)
|
22
24
|
# end
|
23
25
|
#
|
24
|
-
# To make the desired methods active while parsing the desired method should
|
25
|
-
# methods remain private they will
|
26
|
+
# To make the desired methods active while parsing the desired method should
|
27
|
+
# be made public in the subclasses. If the methods remain private they will
|
28
|
+
# not be called during parsing.
|
26
29
|
#
|
27
30
|
# def hash_start(key); end
|
28
31
|
# def hash_end(key); end
|
@@ -36,9 +39,9 @@ module Oj
|
|
36
39
|
def initialize()
|
37
40
|
end
|
38
41
|
|
39
|
-
# To make the desired methods active while parsing the desired method
|
40
|
-
#
|
41
|
-
#
|
42
|
+
# To make the desired methods active while parsing the desired method should
|
43
|
+
# be made public in the subclasses. If the methods remain private they will
|
44
|
+
# not be called during parsing.
|
42
45
|
private
|
43
46
|
|
44
47
|
def hash_start(key)
|
data/lib/oj/schandler.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
module Oj
|
2
|
+
# A Simple Callback Parser (SCP) for JSON. The Oj::ScHandler class should be
|
3
|
+
# subclassed and then used with the Oj.sc_parse() method. The Scp methods will
|
4
|
+
# then be called as the file is parsed. The handler does not have to be a
|
5
|
+
# subclass of the ScHandler class as long as it responds to the desired
|
6
|
+
# methods.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# require 'oj'
|
11
|
+
#
|
12
|
+
# class MyHandler < ::Oj::ScHandler
|
13
|
+
# def hash_start
|
14
|
+
# {}
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# def hash_set(h,k,v)
|
18
|
+
# h[k] = v
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# def array_start
|
22
|
+
# []
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# def array_append(a,v)
|
26
|
+
# a << v
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# def add_value(v)
|
30
|
+
# p v
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# def error(message, line, column)
|
34
|
+
# p "ERROR: #{message}"
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# File.open('any.json', 'r') do |f|
|
39
|
+
# Oj.sc_parse(MyHandler.new, f)
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# To make the desired methods active while parsing the desired method should
|
43
|
+
# be made public in the subclasses. If the methods remain private they will
|
44
|
+
# not be called during parsing.
|
45
|
+
#
|
46
|
+
# def hash_start(); end
|
47
|
+
# def hash_end(); end
|
48
|
+
# def hash_key(key); end
|
49
|
+
# def hash_set(h, key, value); end
|
50
|
+
# def array_start(); end
|
51
|
+
# def array_end(); end
|
52
|
+
# def array_append(a, value); end
|
53
|
+
# def add_value(value); end
|
54
|
+
#
|
55
|
+
# As certain elements of a JSON document are reached during parsing the
|
56
|
+
# callbacks are called. The parser helps by keeping track of objects created
|
57
|
+
# by the callbacks but does not create those objects itself.
|
58
|
+
#
|
59
|
+
# hash_start
|
60
|
+
#
|
61
|
+
# When a JSON object element starts the hash_start() callback is called if
|
62
|
+
# public. It should return what ever Ruby Object is to be used as the element
|
63
|
+
# that will later be included in the hash_set() callback.
|
64
|
+
#
|
65
|
+
# hash_end
|
66
|
+
#
|
67
|
+
# When a hash key is encountered the hash_key method is called with the parsed
|
68
|
+
# hash value key. The return value from the call is then used as the key in
|
69
|
+
# the key-value pair that follows.
|
70
|
+
#
|
71
|
+
# hash_key
|
72
|
+
#
|
73
|
+
# At the end of a JSON object element the hash_end() callback is called if public.
|
74
|
+
#
|
75
|
+
# hash_set
|
76
|
+
#
|
77
|
+
# When a key value pair is encountered during parsing the hash_set() callback
|
78
|
+
# is called if public. The first element will be the object returned from the
|
79
|
+
# enclosing hash_start() callback. The second argument is the key and the last
|
80
|
+
# is the value.
|
81
|
+
#
|
82
|
+
# array_start
|
83
|
+
#
|
84
|
+
# When a JSON array element is started the array_start() callback is called if
|
85
|
+
# public. It should return what ever Ruby Object is to be used as the element
|
86
|
+
# that will later be included in the array_append() callback.
|
87
|
+
#
|
88
|
+
# array_end
|
89
|
+
#
|
90
|
+
# At the end of a JSON array element the array_end() callback is called if public.
|
91
|
+
#
|
92
|
+
# array_append
|
93
|
+
#
|
94
|
+
# When a element is encountered that is an element of an array the
|
95
|
+
# array_append() callback is called if public. The first argument to the
|
96
|
+
# callback is the Ruby object returned from the enclosing array_start()
|
97
|
+
# callback.
|
98
|
+
#
|
99
|
+
# add_value
|
100
|
+
#
|
101
|
+
# The handler is expected to handle multiple JSON elements in one stream,
|
102
|
+
# file, or string. When a top level JSON has been read completely the
|
103
|
+
# add_value() callback is called. Even if only one element was ready this
|
104
|
+
# callback returns the Ruby object that was constructed during the parsing.
|
105
|
+
#
|
106
|
+
class ScHandler
|
107
|
+
# Create a new instance of the ScHandler class.
|
108
|
+
def initialize()
|
109
|
+
end
|
110
|
+
|
111
|
+
# To make the desired methods active while parsing the desired method should
|
112
|
+
# be made public in the subclasses. If the methods remain private they will
|
113
|
+
# not be called during parsing.
|
114
|
+
private
|
115
|
+
|
116
|
+
def hash_start()
|
117
|
+
end
|
118
|
+
|
119
|
+
def hash_end()
|
120
|
+
end
|
121
|
+
|
122
|
+
def hash_key(key)
|
123
|
+
key
|
124
|
+
end
|
125
|
+
|
126
|
+
def hash_set(h, key, value)
|
127
|
+
end
|
128
|
+
|
129
|
+
def array_start()
|
130
|
+
end
|
131
|
+
|
132
|
+
def array_end()
|
133
|
+
end
|
134
|
+
|
135
|
+
def add_value(value)
|
136
|
+
end
|
137
|
+
|
138
|
+
def array_append(a, value)
|
139
|
+
end
|
140
|
+
|
141
|
+
end # ScHandler
|
142
|
+
end # Oj
|
data/lib/oj/state.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
|
2
|
+
module JSON
|
3
|
+
module Ext
|
4
|
+
module Generator
|
5
|
+
unless defined?(::JSON::Ext::Generator::State)
|
6
|
+
# This class exists for json gem compatibility only. While it can be
|
7
|
+
# used as the options for other than compatibility a simple Hash is
|
8
|
+
# recommended as it is simpler and performs better. The only bit
|
9
|
+
# missing by not using a state object is the depth availability which
|
10
|
+
# may be the depth during dumping or maybe not since it can be set and
|
11
|
+
# the docs for depth= is the same as max_nesting. Note: Had to make
|
12
|
+
# this a subclass of Object instead of Hash like EashyHash due to
|
13
|
+
# conflicts with the json gem.
|
14
|
+
class State
|
15
|
+
|
16
|
+
def self.from_state(opts)
|
17
|
+
s = self.new()
|
18
|
+
s.clear()
|
19
|
+
s.merge(opts)
|
20
|
+
s
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(opts = {})
|
24
|
+
@attrs = {}
|
25
|
+
|
26
|
+
# Populate with all vars then merge in opts. This class deviates from
|
27
|
+
# the json gem in that any of the options can be set with the opts
|
28
|
+
# argument. The json gem limits the opts use to 7 of the options.
|
29
|
+
@attrs[:indent] = ''
|
30
|
+
@attrs[:space] = ''
|
31
|
+
@attrs[:space_before] = ''
|
32
|
+
@attrs[:array_nl] = ''
|
33
|
+
@attrs[:object_nl] = ''
|
34
|
+
@attrs[:allow_nan] = false
|
35
|
+
@attrs[:buffer_initial_length] = 1024 # completely ignored by Oj
|
36
|
+
@attrs[:depth] = 0
|
37
|
+
@attrs[:max_nesting] = 100
|
38
|
+
@attrs[:check_circular?] = true
|
39
|
+
@attrs[:ascii_only] = false
|
40
|
+
|
41
|
+
@attrs.merge!(opts)
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_h()
|
45
|
+
return @attrs.dup
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_hash()
|
49
|
+
return @attrs.dup
|
50
|
+
end
|
51
|
+
|
52
|
+
def allow_nan?()
|
53
|
+
@attrs[:allow_nan]
|
54
|
+
end
|
55
|
+
|
56
|
+
def ascii_only?()
|
57
|
+
@attrs[:ascii_only]
|
58
|
+
end
|
59
|
+
|
60
|
+
def configure(opts)
|
61
|
+
raise TypeError.new('expected a Hash') unless opts.respond_to?(:to_h)
|
62
|
+
@attrs.merge!(opts.to_h)
|
63
|
+
end
|
64
|
+
|
65
|
+
def generate(obj)
|
66
|
+
JSON.generate(obj)
|
67
|
+
end
|
68
|
+
|
69
|
+
def merge(opts)
|
70
|
+
@attrs.merge!(opts)
|
71
|
+
end
|
72
|
+
|
73
|
+
# special rule for this.
|
74
|
+
def buffer_initial_length=(len)
|
75
|
+
len = 1024 if 0 >= len
|
76
|
+
@attrs[:buffer_initial_length] = len
|
77
|
+
end
|
78
|
+
|
79
|
+
# Replaces the Object.respond_to?() method.
|
80
|
+
# @param [Symbol] m method symbol
|
81
|
+
# @return [Boolean] true for any method that matches an instance
|
82
|
+
# variable reader, otherwise false.
|
83
|
+
def respond_to?(m)
|
84
|
+
return true if super
|
85
|
+
return true if has_key?(key)
|
86
|
+
return true if has_key?(key.to_s)
|
87
|
+
has_key?(key.to_sym)
|
88
|
+
end
|
89
|
+
|
90
|
+
def [](key)
|
91
|
+
key = key.to_sym
|
92
|
+
@attrs.fetch(key, nil)
|
93
|
+
end
|
94
|
+
|
95
|
+
def []=(key, value)
|
96
|
+
key = key.to_sym
|
97
|
+
@attrs[key] = value
|
98
|
+
end
|
99
|
+
|
100
|
+
def clear()
|
101
|
+
@attrs.clear()
|
102
|
+
end
|
103
|
+
|
104
|
+
def has_key?(k)
|
105
|
+
@attrs.has_key?(key.to_sym)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Handles requests for Hash values. Others cause an Exception to be raised.
|
109
|
+
# @param [Symbol|String] m method symbol
|
110
|
+
# @return [Boolean] the value of the specified instance variable.
|
111
|
+
# @raise [ArgumentError] if an argument is given. Zero arguments expected.
|
112
|
+
# @raise [NoMethodError] if the instance variable is not defined.
|
113
|
+
def method_missing(m, *args, &block)
|
114
|
+
if m.to_s.end_with?('=')
|
115
|
+
raise ArgumentError.new("wrong number of arguments (#{args.size} for 1 with #{m}) to method #{m}") if args.nil? or 1 != args.length
|
116
|
+
m = m.to_s[0..-2]
|
117
|
+
m = m.to_sym
|
118
|
+
return @attrs.store(m, args[0])
|
119
|
+
else
|
120
|
+
raise ArgumentError.new("wrong number of arguments (#{args.size} for 0 with #{m}) to method #{m}") unless args.nil? or args.empty?
|
121
|
+
return @attrs[m.to_sym]
|
122
|
+
end
|
123
|
+
raise NoMethodError.new("undefined method #{m}", m)
|
124
|
+
end
|
125
|
+
|
126
|
+
end # State
|
127
|
+
end # defined check
|
128
|
+
end # Generator
|
129
|
+
end # Ext
|
130
|
+
|
131
|
+
end # JSON
|
data/lib/oj/version.rb
CHANGED
data/lib/oj.rb
CHANGED
@@ -1,33 +1,21 @@
|
|
1
|
-
|
2
|
-
# optimized JSON handling.
|
3
|
-
#
|
4
|
-
# Oj has several dump or serialization modes which control how Objects are
|
5
|
-
# converted to JSON. These modes are set with the :mode option in either the
|
6
|
-
# default options or as one of the options to the dump() method.
|
7
|
-
#
|
8
|
-
# - :strict mode will only allow the 7 basic JSON types to be serialized. Any other Object
|
9
|
-
# will raise and Exception.
|
10
|
-
#
|
11
|
-
# - :null mode replaces any Object that is not one of the JSON types is replaced by a JSON null.
|
12
|
-
#
|
13
|
-
# - :object mode will dump any Object as a JSON Object with keys that match
|
14
|
-
# the Ruby Object's variable names without the '@' character. This is the
|
15
|
-
# highest performance mode.
|
16
|
-
#
|
17
|
-
# - :compat mode is is the compatible with other systems. It will serialize
|
18
|
-
# any Object but will check to see if the Object implements a to_hash() or
|
19
|
-
# to_json() method. If either exists that method is used for serializing the
|
20
|
-
# Object. The to_hash() is more flexible and produces more consistent output
|
21
|
-
# so it has a preference over the to_json() method. If neither the to_json()
|
22
|
-
# or to_hash() methods exist then the Oj internal Object variable encoding
|
23
|
-
# is used.
|
1
|
+
|
24
2
|
module Oj
|
25
3
|
end
|
26
4
|
|
5
|
+
begin
|
6
|
+
# This require exists to get around Rubinius failing to load bigdecimal from
|
7
|
+
# the C extension.
|
8
|
+
require 'bigdecimal'
|
9
|
+
rescue Exception
|
10
|
+
# ignore
|
11
|
+
end
|
12
|
+
|
27
13
|
require 'oj/version'
|
28
14
|
require 'oj/bag'
|
15
|
+
require 'oj/easy_hash'
|
29
16
|
require 'oj/error'
|
30
17
|
require 'oj/mimic'
|
31
18
|
require 'oj/saj'
|
19
|
+
require 'oj/schandler'
|
32
20
|
|
33
21
|
require 'oj/oj' # C extension
|
data/pages/Advanced.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Advanced Features
|
2
|
+
|
3
|
+
Optimized JSON (Oj), as the name implies, was written to provide speed optimized
|
4
|
+
JSON handling. It was designed as a faster alternative to Yajl and other
|
5
|
+
common Ruby JSON parsers. So far it has achieved that, and is about 2 times faster
|
6
|
+
than any other Ruby JSON parser, and 3 or more times faster at serializing JSON.
|
7
|
+
|
8
|
+
Oj has several `dump` or serialization modes which control how Ruby `Object`s are
|
9
|
+
converted to JSON. These modes are set with the `:mode` option in either the
|
10
|
+
default options or as one of the options to the `dump` method. In addition to
|
11
|
+
the various options there are also alternative APIs for parsing JSON.
|
12
|
+
|
13
|
+
The fastest alternative parser API is the `Oj::Doc` API. The `Oj::Doc` API takes
|
14
|
+
a completely different approach by opening a JSON document and providing calls
|
15
|
+
to navigate around the JSON while it is open. With this approach, JSON access
|
16
|
+
can be well over 20 times faster than conventional JSON parsing.
|
17
|
+
|
18
|
+
The `Oj::Saj` and `Oj::ScHandler` APIs are callback parsers that
|
19
|
+
walk the JSON document depth first and makes callbacks for each element.
|
20
|
+
Both callback parser are useful when only portions of the JSON are of
|
21
|
+
interest. Performance up to 20 times faster than conventional JSON is
|
22
|
+
possible if only a few elements of the JSON are of interest.
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Compatibility
|
2
|
+
|
3
|
+
**Ruby**
|
4
|
+
|
5
|
+
Oj is compatible with Ruby 1.9.3, 2.0.0, 2.1, 2.2, 2.3, 2.4 and RBX.
|
6
|
+
Support for JRuby has been removed as JRuby no longer supports C extensions and
|
7
|
+
there are bugs in the older versions that are not being fixed.
|
8
|
+
|
9
|
+
**Rails**
|
10
|
+
|
11
|
+
Although up until 4.1 Rails uses [multi_json](https://github.com/intridea/multi_json), an [issue in Rails](https://github.com/rails/rails/issues/9212) causes ActiveSupport to fail to make use Oj for JSON handling.
|
12
|
+
There is a
|
13
|
+
[gem to patch this](https://github.com/GoodLife/rails-patch-json-encode) for
|
14
|
+
Rails 3.2 and 4.0. As of the Oj 2.6.0 release the default behavior is to not use
|
15
|
+
the `to_json()` method unless the `:use_to_json` option is set. This provides
|
16
|
+
another work around to the rails older and newer behavior.
|
17
|
+
|
18
|
+
The latest ActiveRecord is able to work with Oj by simply using the line:
|
19
|
+
|
20
|
+
```
|
21
|
+
serialize :metadata, Oj
|
22
|
+
```
|
23
|
+
|
24
|
+
In version Rails 4.1, multi_json has been removed, and this patch is unnecessary and will no longer work.
|
25
|
+
See {file:Rails.md}.
|