json 1.8.3 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/{CHANGES → CHANGES.md} +241 -90
- data/Gemfile +10 -6
- data/{COPYING-json-jruby → LICENSE} +5 -6
- data/{README.rdoc → README.md} +201 -134
- data/VERSION +1 -1
- data/ext/json/ext/fbuffer/fbuffer.h +0 -3
- data/ext/json/ext/generator/generator.c +264 -104
- data/ext/json/ext/generator/generator.h +12 -4
- data/ext/json/ext/parser/extconf.rb +28 -0
- data/ext/json/ext/parser/parser.c +425 -462
- data/ext/json/ext/parser/parser.h +5 -5
- data/ext/json/ext/parser/parser.rl +181 -181
- data/ext/json/extconf.rb +1 -1
- data/json.gemspec +0 -0
- data/lib/json.rb +550 -29
- data/lib/json/add/bigdecimal.rb +3 -2
- data/lib/json/add/complex.rb +4 -4
- data/lib/json/add/core.rb +1 -0
- data/lib/json/add/date.rb +1 -1
- data/lib/json/add/date_time.rb +1 -1
- data/lib/json/add/exception.rb +1 -1
- data/lib/json/add/ostruct.rb +3 -3
- data/lib/json/add/range.rb +1 -1
- data/lib/json/add/rational.rb +3 -3
- data/lib/json/add/regexp.rb +3 -3
- data/lib/json/add/set.rb +29 -0
- data/lib/json/add/struct.rb +1 -1
- data/lib/json/add/symbol.rb +1 -1
- data/lib/json/add/time.rb +1 -1
- data/lib/json/common.rb +381 -162
- data/lib/json/ext.rb +0 -6
- data/lib/json/generic_object.rb +5 -4
- data/lib/json/pure.rb +2 -8
- data/lib/json/pure/generator.rb +83 -126
- data/lib/json/pure/parser.rb +62 -84
- data/lib/json/version.rb +2 -1
- data/tests/fixtures/fail29.json +1 -0
- data/tests/fixtures/fail30.json +1 -0
- data/tests/fixtures/fail31.json +1 -0
- data/tests/fixtures/fail32.json +1 -0
- data/tests/fixtures/obsolete_fail1.json +1 -0
- data/tests/{test_json_addition.rb → json_addition_test.rb} +28 -25
- data/tests/json_common_interface_test.rb +169 -0
- data/tests/json_encoding_test.rb +107 -0
- data/tests/json_ext_parser_test.rb +15 -0
- data/tests/{test_json_fixtures.rb → json_fixtures_test.rb} +13 -8
- data/tests/{test_json_generate.rb → json_generator_test.rb} +109 -47
- data/tests/{test_json_generic_object.rb → json_generic_object_test.rb} +15 -8
- data/tests/json_parser_test.rb +497 -0
- data/tests/json_string_matching_test.rb +38 -0
- data/tests/lib/core_assertions.rb +763 -0
- data/tests/lib/envutil.rb +365 -0
- data/tests/lib/find_executable.rb +22 -0
- data/tests/lib/helper.rb +4 -0
- data/tests/ractor_test.rb +30 -0
- data/tests/test_helper.rb +17 -0
- metadata +48 -76
- data/.gitignore +0 -16
- data/.travis.yml +0 -26
- data/COPYING +0 -58
- data/GPL +0 -340
- data/README-json-jruby.markdown +0 -33
- data/Rakefile +0 -412
- data/TODO +0 -1
- data/data/example.json +0 -1
- data/data/index.html +0 -38
- data/data/prototype.js +0 -4184
- data/diagrams/.keep +0 -0
- data/install.rb +0 -23
- data/java/src/json/ext/ByteListTranscoder.java +0 -167
- data/java/src/json/ext/Generator.java +0 -444
- data/java/src/json/ext/GeneratorMethods.java +0 -232
- data/java/src/json/ext/GeneratorService.java +0 -43
- data/java/src/json/ext/GeneratorState.java +0 -543
- data/java/src/json/ext/OptionsReader.java +0 -114
- data/java/src/json/ext/Parser.java +0 -2645
- data/java/src/json/ext/Parser.rl +0 -969
- data/java/src/json/ext/ParserService.java +0 -35
- data/java/src/json/ext/RuntimeInfo.java +0 -121
- data/java/src/json/ext/StringDecoder.java +0 -167
- data/java/src/json/ext/StringEncoder.java +0 -106
- data/java/src/json/ext/Utils.java +0 -89
- data/json-java.gemspec +0 -23
- data/json_pure.gemspec +0 -40
- data/tests/fixtures/fail1.json +0 -1
- data/tests/setup_variant.rb +0 -11
- data/tests/test_json.rb +0 -553
- data/tests/test_json_encoding.rb +0 -65
- data/tests/test_json_string_matching.rb +0 -39
- data/tests/test_json_unicode.rb +0 -72
- data/tools/fuzz.rb +0 -139
- data/tools/server.rb +0 -62
data/lib/json/add/bigdecimal.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#frozen_string_literal: false
|
1
2
|
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
2
3
|
require 'json'
|
3
4
|
end
|
@@ -22,7 +23,7 @@ class BigDecimal
|
|
22
23
|
end
|
23
24
|
|
24
25
|
# return the JSON value
|
25
|
-
def to_json(*)
|
26
|
-
as_json.to_json
|
26
|
+
def to_json(*args)
|
27
|
+
as_json.to_json(*args)
|
27
28
|
end
|
28
29
|
end
|
data/lib/json/add/complex.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
+
#frozen_string_literal: false
|
1
2
|
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
2
3
|
require 'json'
|
3
4
|
end
|
4
|
-
defined?(::Complex) or require 'complex'
|
5
5
|
|
6
6
|
class Complex
|
7
7
|
|
@@ -22,7 +22,7 @@ class Complex
|
|
22
22
|
end
|
23
23
|
|
24
24
|
# Stores class name (Complex) along with real value <tt>r</tt> and imaginary value <tt>i</tt> as JSON string
|
25
|
-
def to_json(*)
|
26
|
-
as_json.to_json
|
25
|
+
def to_json(*args)
|
26
|
+
as_json.to_json(*args)
|
27
27
|
end
|
28
|
-
end
|
28
|
+
end
|
data/lib/json/add/core.rb
CHANGED
data/lib/json/add/date.rb
CHANGED
data/lib/json/add/date_time.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
+
#frozen_string_literal: false
|
1
2
|
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
2
3
|
require 'json'
|
3
4
|
end
|
4
5
|
require 'date'
|
5
6
|
|
6
|
-
# DateTime serialization/deserialization
|
7
7
|
class DateTime
|
8
8
|
|
9
9
|
# Deserializes JSON string by converting year <tt>y</tt>, month <tt>m</tt>,
|
data/lib/json/add/exception.rb
CHANGED
data/lib/json/add/ostruct.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
+
#frozen_string_literal: false
|
1
2
|
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
2
3
|
require 'json'
|
3
4
|
end
|
4
5
|
require 'ostruct'
|
5
6
|
|
6
|
-
# OpenStruct serialization/deserialization
|
7
7
|
class OpenStruct
|
8
8
|
|
9
9
|
# Deserializes JSON string by constructing new Struct object with values
|
10
|
-
# <tt>
|
10
|
+
# <tt>t</tt> serialized by <tt>to_json</tt>.
|
11
11
|
def self.json_create(object)
|
12
12
|
new(object['t'] || object[:t])
|
13
13
|
end
|
@@ -23,7 +23,7 @@ class OpenStruct
|
|
23
23
|
}
|
24
24
|
end
|
25
25
|
|
26
|
-
# Stores class name (OpenStruct) with this struct's values <tt>
|
26
|
+
# Stores class name (OpenStruct) with this struct's values <tt>t</tt> as a
|
27
27
|
# JSON string.
|
28
28
|
def to_json(*args)
|
29
29
|
as_json.to_json(*args)
|
data/lib/json/add/range.rb
CHANGED
data/lib/json/add/rational.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
+
#frozen_string_literal: false
|
1
2
|
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
2
3
|
require 'json'
|
3
4
|
end
|
4
|
-
defined?(::Rational) or require 'rational'
|
5
5
|
|
6
6
|
class Rational
|
7
7
|
# Deserializes JSON string by converting numerator value <tt>n</tt>,
|
@@ -21,7 +21,7 @@ class Rational
|
|
21
21
|
end
|
22
22
|
|
23
23
|
# Stores class name (Rational) along with numerator value <tt>n</tt> and denominator value <tt>d</tt> as JSON string
|
24
|
-
def to_json(*)
|
25
|
-
as_json.to_json
|
24
|
+
def to_json(*args)
|
25
|
+
as_json.to_json(*args)
|
26
26
|
end
|
27
27
|
end
|
data/lib/json/add/regexp.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
+
#frozen_string_literal: false
|
1
2
|
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
2
3
|
require 'json'
|
3
4
|
end
|
4
5
|
|
5
|
-
# Regexp serialization/deserialization
|
6
6
|
class Regexp
|
7
7
|
|
8
8
|
# Deserializes JSON string by constructing new Regexp object with source
|
@@ -24,7 +24,7 @@ class Regexp
|
|
24
24
|
|
25
25
|
# Stores class name (Regexp) with options <tt>o</tt> and source <tt>s</tt>
|
26
26
|
# (Regexp or String) as JSON string
|
27
|
-
def to_json(*)
|
28
|
-
as_json.to_json
|
27
|
+
def to_json(*args)
|
28
|
+
as_json.to_json(*args)
|
29
29
|
end
|
30
30
|
end
|
data/lib/json/add/set.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
2
|
+
require 'json'
|
3
|
+
end
|
4
|
+
defined?(::Set) or require 'set'
|
5
|
+
|
6
|
+
class Set
|
7
|
+
# Import a JSON Marshalled object.
|
8
|
+
#
|
9
|
+
# method used for JSON marshalling support.
|
10
|
+
def self.json_create(object)
|
11
|
+
new object['a']
|
12
|
+
end
|
13
|
+
|
14
|
+
# Marshal the object to JSON.
|
15
|
+
#
|
16
|
+
# method used for JSON marshalling support.
|
17
|
+
def as_json(*)
|
18
|
+
{
|
19
|
+
JSON.create_id => self.class.name,
|
20
|
+
'a' => to_a,
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
# return the JSON value
|
25
|
+
def to_json(*args)
|
26
|
+
as_json.to_json(*args)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
data/lib/json/add/struct.rb
CHANGED
data/lib/json/add/symbol.rb
CHANGED
data/lib/json/add/time.rb
CHANGED
data/lib/json/common.rb
CHANGED
@@ -1,14 +1,20 @@
|
|
1
|
+
#frozen_string_literal: false
|
1
2
|
require 'json/version'
|
2
3
|
require 'json/generic_object'
|
3
4
|
|
4
5
|
module JSON
|
5
6
|
class << self
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# data structure object and return it.
|
7
|
+
# :call-seq:
|
8
|
+
# JSON[object] -> new_array or new_string
|
9
9
|
#
|
10
|
-
#
|
11
|
-
#
|
10
|
+
# If +object+ is a \String,
|
11
|
+
# calls JSON.parse with +object+ and +opts+ (see method #parse):
|
12
|
+
# json = '[0, 1, null]'
|
13
|
+
# JSON[json]# => [0, 1, nil]
|
14
|
+
#
|
15
|
+
# Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate):
|
16
|
+
# ruby = [0, 1, nil]
|
17
|
+
# JSON[ruby] # => '[0,1,null]'
|
12
18
|
def [](object, opts = {})
|
13
19
|
if object.respond_to? :to_str
|
14
20
|
JSON.parse(object.to_str, opts)
|
@@ -18,13 +24,14 @@ module JSON
|
|
18
24
|
end
|
19
25
|
|
20
26
|
# Returns the JSON parser class that is used by JSON. This is either
|
21
|
-
# JSON::Ext::Parser or JSON::Pure::Parser
|
27
|
+
# JSON::Ext::Parser or JSON::Pure::Parser:
|
28
|
+
# JSON.parser # => JSON::Ext::Parser
|
22
29
|
attr_reader :parser
|
23
30
|
|
24
31
|
# Set the JSON parser class _parser_ to be used by JSON.
|
25
32
|
def parser=(parser) # :nodoc:
|
26
33
|
@parser = parser
|
27
|
-
remove_const :Parser if
|
34
|
+
remove_const :Parser if const_defined?(:Parser, false)
|
28
35
|
const_set :Parser, parser
|
29
36
|
end
|
30
37
|
|
@@ -35,8 +42,8 @@ module JSON
|
|
35
42
|
def deep_const_get(path) # :nodoc:
|
36
43
|
path.to_s.split(/::/).inject(Object) do |p, c|
|
37
44
|
case
|
38
|
-
when c.empty?
|
39
|
-
when
|
45
|
+
when c.empty? then p
|
46
|
+
when p.const_defined?(c, true) then p.const_get(c)
|
40
47
|
else
|
41
48
|
begin
|
42
49
|
p.const_missing(c)
|
@@ -64,37 +71,61 @@ module JSON
|
|
64
71
|
end
|
65
72
|
self.state = generator::State
|
66
73
|
const_set :State, self.state
|
67
|
-
const_set :SAFE_STATE_PROTOTYPE, State.new
|
68
|
-
const_set :FAST_STATE_PROTOTYPE,
|
74
|
+
const_set :SAFE_STATE_PROTOTYPE, State.new # for JRuby
|
75
|
+
const_set :FAST_STATE_PROTOTYPE, create_fast_state
|
76
|
+
const_set :PRETTY_STATE_PROTOTYPE, create_pretty_state
|
77
|
+
ensure
|
78
|
+
$VERBOSE = old
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_fast_state
|
82
|
+
State.new(
|
69
83
|
:indent => '',
|
70
84
|
:space => '',
|
71
85
|
:object_nl => "",
|
72
86
|
:array_nl => "",
|
73
87
|
:max_nesting => false
|
74
88
|
)
|
75
|
-
|
89
|
+
end
|
90
|
+
|
91
|
+
def create_pretty_state
|
92
|
+
State.new(
|
76
93
|
:indent => ' ',
|
77
94
|
:space => ' ',
|
78
95
|
:object_nl => "\n",
|
79
96
|
:array_nl => "\n"
|
80
97
|
)
|
81
|
-
ensure
|
82
|
-
$VERBOSE = old
|
83
98
|
end
|
84
99
|
|
85
100
|
# Returns the JSON generator module that is used by JSON. This is
|
86
|
-
# either JSON::Ext::Generator or JSON::Pure::Generator
|
101
|
+
# either JSON::Ext::Generator or JSON::Pure::Generator:
|
102
|
+
# JSON.generator # => JSON::Ext::Generator
|
87
103
|
attr_reader :generator
|
88
104
|
|
89
|
-
# Returns the JSON generator state class that is used by JSON. This is
|
90
|
-
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State
|
105
|
+
# Sets or Returns the JSON generator state class that is used by JSON. This is
|
106
|
+
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
|
107
|
+
# JSON.state # => JSON::Ext::Generator::State
|
91
108
|
attr_accessor :state
|
109
|
+
end
|
92
110
|
|
93
|
-
|
94
|
-
|
95
|
-
|
111
|
+
DEFAULT_CREATE_ID = 'json_class'.freeze
|
112
|
+
private_constant :DEFAULT_CREATE_ID
|
113
|
+
|
114
|
+
CREATE_ID_TLS_KEY = "JSON.create_id".freeze
|
115
|
+
private_constant :CREATE_ID_TLS_KEY
|
116
|
+
|
117
|
+
# Sets create identifier, which is used to decide if the _json_create_
|
118
|
+
# hook of a class should be called; initial value is +json_class+:
|
119
|
+
# JSON.create_id # => 'json_class'
|
120
|
+
def self.create_id=(new_value)
|
121
|
+
Thread.current[CREATE_ID_TLS_KEY] = new_value.dup.freeze
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns the current create identifier.
|
125
|
+
# See also JSON.create_id=.
|
126
|
+
def self.create_id
|
127
|
+
Thread.current[CREATE_ID_TLS_KEY] || DEFAULT_CREATE_ID
|
96
128
|
end
|
97
|
-
self.create_id = 'json_class'
|
98
129
|
|
99
130
|
NaN = 0.0/0
|
100
131
|
|
@@ -125,7 +156,7 @@ module JSON
|
|
125
156
|
# This exception is raised if a generator or unparser error occurs.
|
126
157
|
class GeneratorError < JSONError; end
|
127
158
|
# For backwards compatibility
|
128
|
-
UnparserError = GeneratorError
|
159
|
+
UnparserError = GeneratorError # :nodoc:
|
129
160
|
|
130
161
|
# This exception is raised if the required unicode support is missing on the
|
131
162
|
# system. Usually this means that the iconv library is not installed.
|
@@ -133,82 +164,140 @@ module JSON
|
|
133
164
|
|
134
165
|
module_function
|
135
166
|
|
136
|
-
#
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
153
|
-
#
|
167
|
+
# :call-seq:
|
168
|
+
# JSON.parse(source, opts) -> object
|
169
|
+
#
|
170
|
+
# Returns the Ruby objects created by parsing the given +source+.
|
171
|
+
#
|
172
|
+
# Argument +source+ contains the \String to be parsed.
|
173
|
+
#
|
174
|
+
# Argument +opts+, if given, contains a \Hash of options for the parsing.
|
175
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
176
|
+
#
|
177
|
+
# ---
|
178
|
+
#
|
179
|
+
# When +source+ is a \JSON array, returns a Ruby \Array:
|
180
|
+
# source = '["foo", 1.0, true, false, null]'
|
181
|
+
# ruby = JSON.parse(source)
|
182
|
+
# ruby # => ["foo", 1.0, true, false, nil]
|
183
|
+
# ruby.class # => Array
|
184
|
+
#
|
185
|
+
# When +source+ is a \JSON object, returns a Ruby \Hash:
|
186
|
+
# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
|
187
|
+
# ruby = JSON.parse(source)
|
188
|
+
# ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
|
189
|
+
# ruby.class # => Hash
|
190
|
+
#
|
191
|
+
# For examples of parsing for all \JSON data types, see
|
192
|
+
# {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
|
193
|
+
#
|
194
|
+
# Parses nested JSON objects:
|
195
|
+
# source = <<-EOT
|
196
|
+
# {
|
197
|
+
# "name": "Dave",
|
198
|
+
# "age" :40,
|
199
|
+
# "hats": [
|
200
|
+
# "Cattleman's",
|
201
|
+
# "Panama",
|
202
|
+
# "Tophat"
|
203
|
+
# ]
|
204
|
+
# }
|
205
|
+
# EOT
|
206
|
+
# ruby = JSON.parse(source)
|
207
|
+
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
208
|
+
#
|
209
|
+
# ---
|
210
|
+
#
|
211
|
+
# Raises an exception if +source+ is not valid JSON:
|
212
|
+
# # Raises JSON::ParserError (783: unexpected token at ''):
|
213
|
+
# JSON.parse('')
|
214
|
+
#
|
154
215
|
def parse(source, opts = {})
|
155
|
-
Parser.new(source, opts).parse
|
216
|
+
Parser.new(source, **(opts||{})).parse
|
156
217
|
end
|
157
218
|
|
158
|
-
#
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
# to true.
|
170
|
-
# * *create_additions*: If set to false, the Parser doesn't create
|
171
|
-
# additions even if a matching class and create_id was found. This option
|
172
|
-
# defaults to false.
|
219
|
+
# :call-seq:
|
220
|
+
# JSON.parse!(source, opts) -> object
|
221
|
+
#
|
222
|
+
# Calls
|
223
|
+
# parse(source, opts)
|
224
|
+
# with +source+ and possibly modified +opts+.
|
225
|
+
#
|
226
|
+
# Differences from JSON.parse:
|
227
|
+
# - Option +max_nesting+, if not provided, defaults to +false+,
|
228
|
+
# which disables checking for nesting depth.
|
229
|
+
# - Option +allow_nan+, if not provided, defaults to +true+.
|
173
230
|
def parse!(source, opts = {})
|
174
231
|
opts = {
|
175
232
|
:max_nesting => false,
|
176
233
|
:allow_nan => true
|
177
|
-
}.
|
178
|
-
Parser.new(source, opts).parse
|
234
|
+
}.merge(opts)
|
235
|
+
Parser.new(source, **(opts||{})).parse
|
236
|
+
end
|
237
|
+
|
238
|
+
# :call-seq:
|
239
|
+
# JSON.load_file(path, opts={}) -> object
|
240
|
+
#
|
241
|
+
# Calls:
|
242
|
+
# parse(File.read(path), opts)
|
243
|
+
#
|
244
|
+
# See method #parse.
|
245
|
+
def load_file(filespec, opts = {})
|
246
|
+
parse(File.read(filespec), opts)
|
179
247
|
end
|
180
248
|
|
181
|
-
#
|
182
|
-
#
|
183
|
-
#
|
184
|
-
#
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
#
|
193
|
-
#
|
194
|
-
#
|
195
|
-
#
|
196
|
-
#
|
197
|
-
#
|
198
|
-
#
|
199
|
-
#
|
200
|
-
#
|
201
|
-
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
#
|
205
|
-
#
|
206
|
-
#
|
249
|
+
# :call-seq:
|
250
|
+
# JSON.load_file!(path, opts = {})
|
251
|
+
#
|
252
|
+
# Calls:
|
253
|
+
# JSON.parse!(File.read(path, opts))
|
254
|
+
#
|
255
|
+
# See method #parse!
|
256
|
+
def load_file!(filespec, opts = {})
|
257
|
+
parse!(File.read(filespec), opts)
|
258
|
+
end
|
259
|
+
|
260
|
+
# :call-seq:
|
261
|
+
# JSON.generate(obj, opts = nil) -> new_string
|
262
|
+
#
|
263
|
+
# Returns a \String containing the generated \JSON data.
|
264
|
+
#
|
265
|
+
# See also JSON.fast_generate, JSON.pretty_generate.
|
266
|
+
#
|
267
|
+
# Argument +obj+ is the Ruby object to be converted to \JSON.
|
268
|
+
#
|
269
|
+
# Argument +opts+, if given, contains a \Hash of options for the generation.
|
270
|
+
# See {Generating Options}[#module-JSON-label-Generating+Options].
|
271
|
+
#
|
272
|
+
# ---
|
273
|
+
#
|
274
|
+
# When +obj+ is an \Array, returns a \String containing a \JSON array:
|
275
|
+
# obj = ["foo", 1.0, true, false, nil]
|
276
|
+
# json = JSON.generate(obj)
|
277
|
+
# json # => '["foo",1.0,true,false,null]'
|
278
|
+
#
|
279
|
+
# When +obj+ is a \Hash, returns a \String containing a \JSON object:
|
280
|
+
# obj = {foo: 0, bar: 's', baz: :bat}
|
281
|
+
# json = JSON.generate(obj)
|
282
|
+
# json # => '{"foo":0,"bar":"s","baz":"bat"}'
|
283
|
+
#
|
284
|
+
# For examples of generating from other Ruby objects, see
|
285
|
+
# {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects].
|
286
|
+
#
|
287
|
+
# ---
|
288
|
+
#
|
289
|
+
# Raises an exception if any formatting option is not a \String.
|
290
|
+
#
|
291
|
+
# Raises an exception if +obj+ contains circular references:
|
292
|
+
# a = []; b = []; a.push(b); b.push(a)
|
293
|
+
# # Raises JSON::NestingError (nesting of 100 is too deep):
|
294
|
+
# JSON.generate(a)
|
295
|
+
#
|
207
296
|
def generate(obj, opts = nil)
|
208
297
|
if State === opts
|
209
298
|
state, opts = opts, nil
|
210
299
|
else
|
211
|
-
state =
|
300
|
+
state = State.new
|
212
301
|
end
|
213
302
|
if opts
|
214
303
|
if opts.respond_to? :to_hash
|
@@ -230,16 +319,24 @@ module JSON
|
|
230
319
|
module_function :unparse
|
231
320
|
# :startdoc:
|
232
321
|
|
233
|
-
#
|
234
|
-
#
|
322
|
+
# :call-seq:
|
323
|
+
# JSON.fast_generate(obj, opts) -> new_string
|
235
324
|
#
|
236
|
-
#
|
237
|
-
#
|
325
|
+
# Arguments +obj+ and +opts+ here are the same as
|
326
|
+
# arguments +obj+ and +opts+ in JSON.generate.
|
327
|
+
#
|
328
|
+
# By default, generates \JSON data without checking
|
329
|
+
# for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
|
330
|
+
#
|
331
|
+
# Raises an exception if +obj+ contains circular references:
|
332
|
+
# a = []; b = []; a.push(b); b.push(a)
|
333
|
+
# # Raises SystemStackError (stack level too deep):
|
334
|
+
# JSON.fast_generate(a)
|
238
335
|
def fast_generate(obj, opts = nil)
|
239
336
|
if State === opts
|
240
337
|
state, opts = opts, nil
|
241
338
|
else
|
242
|
-
state =
|
339
|
+
state = JSON.create_fast_state
|
243
340
|
end
|
244
341
|
if opts
|
245
342
|
if opts.respond_to? :to_hash
|
@@ -260,17 +357,41 @@ module JSON
|
|
260
357
|
module_function :fast_unparse
|
261
358
|
# :startdoc:
|
262
359
|
|
263
|
-
#
|
264
|
-
#
|
265
|
-
#
|
360
|
+
# :call-seq:
|
361
|
+
# JSON.pretty_generate(obj, opts = nil) -> new_string
|
362
|
+
#
|
363
|
+
# Arguments +obj+ and +opts+ here are the same as
|
364
|
+
# arguments +obj+ and +opts+ in JSON.generate.
|
365
|
+
#
|
366
|
+
# Default options are:
|
367
|
+
# {
|
368
|
+
# indent: ' ', # Two spaces
|
369
|
+
# space: ' ', # One space
|
370
|
+
# array_nl: "\n", # Newline
|
371
|
+
# object_nl: "\n" # Newline
|
372
|
+
# }
|
373
|
+
#
|
374
|
+
# Example:
|
375
|
+
# obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
|
376
|
+
# json = JSON.pretty_generate(obj)
|
377
|
+
# puts json
|
378
|
+
# Output:
|
379
|
+
# {
|
380
|
+
# "foo": [
|
381
|
+
# "bar",
|
382
|
+
# "baz"
|
383
|
+
# ],
|
384
|
+
# "bat": {
|
385
|
+
# "bam": 0,
|
386
|
+
# "bad": 1
|
387
|
+
# }
|
388
|
+
# }
|
266
389
|
#
|
267
|
-
# The _opts_ argument can be used to configure the generator. See the
|
268
|
-
# generate method for a more detailed explanation.
|
269
390
|
def pretty_generate(obj, opts = nil)
|
270
391
|
if State === opts
|
271
392
|
state, opts = opts, nil
|
272
393
|
else
|
273
|
-
state =
|
394
|
+
state = JSON.create_pretty_state
|
274
395
|
end
|
275
396
|
if opts
|
276
397
|
if opts.respond_to? :to_hash
|
@@ -292,33 +413,147 @@ module JSON
|
|
292
413
|
# :startdoc:
|
293
414
|
|
294
415
|
class << self
|
295
|
-
#
|
296
|
-
#
|
297
|
-
#
|
298
|
-
#
|
416
|
+
# Sets or returns default options for the JSON.load method.
|
417
|
+
# Initially:
|
418
|
+
# opts = JSON.load_default_options
|
419
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
|
299
420
|
attr_accessor :load_default_options
|
300
421
|
end
|
301
422
|
self.load_default_options = {
|
302
423
|
:max_nesting => false,
|
303
424
|
:allow_nan => true,
|
304
|
-
:
|
425
|
+
:allow_blank => true,
|
305
426
|
:create_additions => true,
|
306
427
|
}
|
307
428
|
|
308
|
-
#
|
309
|
-
#
|
310
|
-
#
|
311
|
-
#
|
312
|
-
#
|
429
|
+
# :call-seq:
|
430
|
+
# JSON.load(source, proc = nil, options = {}) -> object
|
431
|
+
#
|
432
|
+
# Returns the Ruby objects created by parsing the given +source+.
|
433
|
+
#
|
434
|
+
# - Argument +source+ must be, or be convertible to, a \String:
|
435
|
+
# - If +source+ responds to instance method +to_str+,
|
436
|
+
# <tt>source.to_str</tt> becomes the source.
|
437
|
+
# - If +source+ responds to instance method +to_io+,
|
438
|
+
# <tt>source.to_io.read</tt> becomes the source.
|
439
|
+
# - If +source+ responds to instance method +read+,
|
440
|
+
# <tt>source.read</tt> becomes the source.
|
441
|
+
# - If both of the following are true, source becomes the \String <tt>'null'</tt>:
|
442
|
+
# - Option +allow_blank+ specifies a truthy value.
|
443
|
+
# - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
|
444
|
+
# - Otherwise, +source+ remains the source.
|
445
|
+
# - Argument +proc+, if given, must be a \Proc that accepts one argument.
|
446
|
+
# It will be called recursively with each result (depth-first order).
|
447
|
+
# See details below.
|
448
|
+
# BEWARE: This method is meant to serialise data from trusted user input,
|
449
|
+
# like from your own database server or clients under your control, it could
|
450
|
+
# be dangerous to allow untrusted users to pass JSON sources into it.
|
451
|
+
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
452
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
453
|
+
# The default options can be changed via method JSON.load_default_options=.
|
454
|
+
#
|
455
|
+
# ---
|
456
|
+
#
|
457
|
+
# When no +proc+ is given, modifies +source+ as above and returns the result of
|
458
|
+
# <tt>parse(source, opts)</tt>; see #parse.
|
313
459
|
#
|
314
|
-
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
#
|
318
|
-
#
|
460
|
+
# Source for following examples:
|
461
|
+
# source = <<-EOT
|
462
|
+
# {
|
463
|
+
# "name": "Dave",
|
464
|
+
# "age" :40,
|
465
|
+
# "hats": [
|
466
|
+
# "Cattleman's",
|
467
|
+
# "Panama",
|
468
|
+
# "Tophat"
|
469
|
+
# ]
|
470
|
+
# }
|
471
|
+
# EOT
|
472
|
+
#
|
473
|
+
# Load a \String:
|
474
|
+
# ruby = JSON.load(source)
|
475
|
+
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
476
|
+
#
|
477
|
+
# Load an \IO object:
|
478
|
+
# require 'stringio'
|
479
|
+
# object = JSON.load(StringIO.new(source))
|
480
|
+
# object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
481
|
+
#
|
482
|
+
# Load a \File object:
|
483
|
+
# path = 't.json'
|
484
|
+
# File.write(path, source)
|
485
|
+
# File.open(path) do |file|
|
486
|
+
# JSON.load(file)
|
487
|
+
# end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
488
|
+
#
|
489
|
+
# ---
|
490
|
+
#
|
491
|
+
# When +proc+ is given:
|
492
|
+
# - Modifies +source+ as above.
|
493
|
+
# - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
|
494
|
+
# - Recursively calls <tt>proc(result)</tt>.
|
495
|
+
# - Returns the final result.
|
496
|
+
#
|
497
|
+
# Example:
|
498
|
+
# require 'json'
|
499
|
+
#
|
500
|
+
# # Some classes for the example.
|
501
|
+
# class Base
|
502
|
+
# def initialize(attributes)
|
503
|
+
# @attributes = attributes
|
504
|
+
# end
|
505
|
+
# end
|
506
|
+
# class User < Base; end
|
507
|
+
# class Account < Base; end
|
508
|
+
# class Admin < Base; end
|
509
|
+
# # The JSON source.
|
510
|
+
# json = <<-EOF
|
511
|
+
# {
|
512
|
+
# "users": [
|
513
|
+
# {"type": "User", "username": "jane", "email": "jane@example.com"},
|
514
|
+
# {"type": "User", "username": "john", "email": "john@example.com"}
|
515
|
+
# ],
|
516
|
+
# "accounts": [
|
517
|
+
# {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
|
518
|
+
# {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
|
519
|
+
# ],
|
520
|
+
# "admins": {"type": "Admin", "password": "0wn3d"}
|
521
|
+
# }
|
522
|
+
# EOF
|
523
|
+
# # Deserializer method.
|
524
|
+
# def deserialize_obj(obj, safe_types = %w(User Account Admin))
|
525
|
+
# type = obj.is_a?(Hash) && obj["type"]
|
526
|
+
# safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
|
527
|
+
# end
|
528
|
+
# # Call to JSON.load
|
529
|
+
# ruby = JSON.load(json, proc {|obj|
|
530
|
+
# case obj
|
531
|
+
# when Hash
|
532
|
+
# obj.each {|k, v| obj[k] = deserialize_obj v }
|
533
|
+
# when Array
|
534
|
+
# obj.map! {|v| deserialize_obj v }
|
535
|
+
# end
|
536
|
+
# })
|
537
|
+
# pp ruby
|
538
|
+
# Output:
|
539
|
+
# {"users"=>
|
540
|
+
# [#<User:0x00000000064c4c98
|
541
|
+
# @attributes=
|
542
|
+
# {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
|
543
|
+
# #<User:0x00000000064c4bd0
|
544
|
+
# @attributes=
|
545
|
+
# {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
|
546
|
+
# "accounts"=>
|
547
|
+
# [{"account"=>
|
548
|
+
# #<Account:0x00000000064c4928
|
549
|
+
# @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
|
550
|
+
# {"account"=>
|
551
|
+
# #<Account:0x00000000064c4680
|
552
|
+
# @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
|
553
|
+
# "admins"=>
|
554
|
+
# #<Admin:0x00000000064c41f8
|
555
|
+
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
319
556
|
#
|
320
|
-
# This method is part of the implementation of the load/dump interface of
|
321
|
-
# Marshal and YAML.
|
322
557
|
def load(source, proc = nil, options = {})
|
323
558
|
opts = load_default_options.merge options
|
324
559
|
if source.respond_to? :to_str
|
@@ -328,7 +563,7 @@ module JSON
|
|
328
563
|
elsif source.respond_to?(:read)
|
329
564
|
source = source.read
|
330
565
|
end
|
331
|
-
if opts[:
|
566
|
+
if opts[:allow_blank] && (source.nil? || source.empty?)
|
332
567
|
source = 'null'
|
333
568
|
end
|
334
569
|
result = parse(source, opts)
|
@@ -337,7 +572,7 @@ module JSON
|
|
337
572
|
end
|
338
573
|
|
339
574
|
# Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
|
340
|
-
def recurse_proc(result, &proc)
|
575
|
+
def recurse_proc(result, &proc) # :nodoc:
|
341
576
|
case result
|
342
577
|
when Array
|
343
578
|
result.each { |x| recurse_proc x, &proc }
|
@@ -354,33 +589,45 @@ module JSON
|
|
354
589
|
module_function :restore
|
355
590
|
|
356
591
|
class << self
|
357
|
-
#
|
358
|
-
#
|
359
|
-
#
|
360
|
-
#
|
592
|
+
# Sets or returns the default options for the JSON.dump method.
|
593
|
+
# Initially:
|
594
|
+
# opts = JSON.dump_default_options
|
595
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :escape_slash=>false}
|
361
596
|
attr_accessor :dump_default_options
|
362
597
|
end
|
363
598
|
self.dump_default_options = {
|
364
599
|
:max_nesting => false,
|
365
600
|
:allow_nan => true,
|
366
|
-
:
|
601
|
+
:escape_slash => false,
|
367
602
|
}
|
368
603
|
|
369
|
-
#
|
370
|
-
#
|
604
|
+
# :call-seq:
|
605
|
+
# JSON.dump(obj, io = nil, limit = nil)
|
606
|
+
#
|
607
|
+
# Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
|
608
|
+
#
|
609
|
+
# The default options can be changed via method JSON.dump_default_options.
|
371
610
|
#
|
372
|
-
#
|
373
|
-
#
|
611
|
+
# - Argument +io+, if given, should respond to method +write+;
|
612
|
+
# the \JSON \String is written to +io+, and +io+ is returned.
|
613
|
+
# If +io+ is not given, the \JSON \String is returned.
|
614
|
+
# - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
|
374
615
|
#
|
375
|
-
#
|
376
|
-
# exception is raised. This argument is similar (but not exactly the
|
377
|
-
# same!) to the _limit_ argument in Marshal.dump.
|
616
|
+
# ---
|
378
617
|
#
|
379
|
-
#
|
380
|
-
#
|
618
|
+
# When argument +io+ is not given, returns the \JSON \String generated from +obj+:
|
619
|
+
# obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
|
620
|
+
# json = JSON.dump(obj)
|
621
|
+
# json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}"
|
381
622
|
#
|
382
|
-
#
|
383
|
-
#
|
623
|
+
# When argument +io+ is given, writes the \JSON \String to +io+ and returns +io+:
|
624
|
+
# path = 't.json'
|
625
|
+
# File.open(path, 'w') do |file|
|
626
|
+
# JSON.dump(obj, file)
|
627
|
+
# end # => #<File:t.json (closed)>
|
628
|
+
# puts File.read(path)
|
629
|
+
# Output:
|
630
|
+
# {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
|
384
631
|
def dump(obj, anIO = nil, limit = nil)
|
385
632
|
if anIO and limit.nil?
|
386
633
|
anIO = anIO.to_io if anIO.respond_to?(:to_io)
|
@@ -402,37 +649,9 @@ module JSON
|
|
402
649
|
raise ArgumentError, "exceed depth limit"
|
403
650
|
end
|
404
651
|
|
405
|
-
#
|
406
|
-
def self.
|
407
|
-
|
408
|
-
break unless string[2 * i + 1]
|
409
|
-
string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
|
410
|
-
end
|
411
|
-
string
|
412
|
-
end
|
413
|
-
|
414
|
-
# Shortcut for iconv.
|
415
|
-
if ::String.method_defined?(:encode)
|
416
|
-
# Encodes string using Ruby's _String.encode_
|
417
|
-
def self.iconv(to, from, string)
|
418
|
-
string.encode(to, from)
|
419
|
-
end
|
420
|
-
else
|
421
|
-
require 'iconv'
|
422
|
-
# Encodes string using _iconv_ library
|
423
|
-
def self.iconv(to, from, string)
|
424
|
-
Iconv.conv(to, from, string)
|
425
|
-
end
|
426
|
-
end
|
427
|
-
|
428
|
-
if ::Object.method(:const_defined?).arity == 1
|
429
|
-
def self.const_defined_in?(modul, constant)
|
430
|
-
modul.const_defined?(constant)
|
431
|
-
end
|
432
|
-
else
|
433
|
-
def self.const_defined_in?(modul, constant)
|
434
|
-
modul.const_defined?(constant, false)
|
435
|
-
end
|
652
|
+
# Encodes string using String.encode.
|
653
|
+
def self.iconv(to, from, string)
|
654
|
+
string.encode(to, from)
|
436
655
|
end
|
437
656
|
end
|
438
657
|
|