39
# Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
40
# UTF16 big endian characters as \u????, and return it.
41
if defined?(::Encoding)
42
def utf8_to_json(string) # :nodoc:
44
string.force_encoding(::Encoding::ASCII_8BIT)
45
string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
46
string.force_encoding(::Encoding::UTF_8)
50
def utf8_to_json_ascii(string) # :nodoc:
52
string.force_encoding(::Encoding::ASCII_8BIT)
53
string.gsub!(/["\\\x0-\x1f]/n) { MAP[$&] }
56
[\xc2-\xdf][\x80-\xbf] |
57
[\xe0-\xef][\x80-\xbf]{2} |
58
[\xf0-\xf4][\x80-\xbf]{3}
60
[\x80-\xc1\xf5-\xff] # invalid
62
c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
63
s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
64
s.force_encoding(::Encoding::ASCII_8BIT)
65
s.gsub!(/.{4}/n, '\\\\u\&')
66
s.force_encoding(::Encoding::UTF_8)
68
string.force_encoding(::Encoding::UTF_8)
71
raise GeneratorError.wrap(e)
74
def utf8_to_json(string) # :nodoc:
75
string.gsub(/["\\\x0-\x1f]/n) { MAP[$&] }
78
def utf8_to_json_ascii(string) # :nodoc:
79
string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
82
[\xc2-\xdf][\x80-\xbf] |
83
[\xe0-\xef][\x80-\xbf]{2} |
84
[\xf0-\xf4][\x80-\xbf]{3}
86
[\x80-\xc1\xf5-\xff] # invalid
88
c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
89
s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
90
s.gsub!(/.{4}/n, '\\\\u\&')
94
raise GeneratorError.wrap(e)
97
module_function :utf8_to_json, :utf8_to_json_ascii
101
# This class is used to create State instances, that are use to hold data
102
# while generating a JSON text from a Ruby data structure.
104
# Creates a State object from _opts_, which ought to be Hash to create
105
# a new State instance configured by _opts_, something else to create
106
# an unconfigured instance. If _opts_ is a State object, it is just
108
def self.from_state(opts)
112
when opts.respond_to?(:to_hash)
114
when opts.respond_to?(:to_h)
117
SAFE_STATE_PROTOTYPE.dup
121
# Instantiates a new State object, configured by _opts_.
123
# _opts_ can have the following keys:
125
# * *indent*: a string used to indent levels (default: ''),
126
# * *space*: a string that is put after, a : or , delimiter (default: ''),
127
# * *space_before*: a string that is put before a : pair delimiter (default: ''),
128
# * *object_nl*: a string that is put at the end of a JSON object (default: ''),
129
# * *array_nl*: a string that is put at the end of a JSON array (default: ''),
130
# * *check_circular*: is deprecated now, use the :max_nesting option instead,
131
# * *max_nesting*: sets the maximum level of data structure nesting in
132
# the generated JSON, max_nesting = 0 if no maximum should be checked.
133
# * *allow_nan*: true if NaN, Infinity, and -Infinity should be
134
# generated, otherwise an exception is thrown, if these values are
135
# encountered. This options defaults to false.
136
# * *quirks_mode*: Enables quirks_mode for parser, that is for example
137
# generating single JSON values instead of documents is possible.
138
def initialize(opts = {})
147
@buffer_initial_length = 1024
151
# This string is used to indent levels in the JSON text.
152
attr_accessor :indent
154
# This string is used to insert a space between the tokens in a JSON
158
# This string is used to insert a space before the ':' in JSON objects.
159
attr_accessor :space_before
161
# This string is put at the end of a line that holds a JSON object (or
163
attr_accessor :object_nl
165
# This string is put at the end of a line that holds a JSON array.
166
attr_accessor :array_nl
168
# This integer returns the maximum level of data structure nesting in
169
# the generated JSON, max_nesting = 0 if no maximum is checked.
170
attr_accessor :max_nesting
172
# If this attribute is set to true, quirks mode is enabled, otherwise
174
attr_accessor :quirks_mode
177
attr_reader :buffer_initial_length
179
def buffer_initial_length=(length)
181
@buffer_initial_length = length
186
# This integer returns the current depth data structure nesting in the
190
def check_max_nesting # :nodoc:
191
return if @max_nesting.zero?
192
current_nesting = depth + 1
193
current_nesting > @max_nesting and
194
raise NestingError, "nesting of #{current_nesting} is too deep"
197
# Returns true, if circular data structures are checked,
198
# otherwise returns false.
203
# Returns true if NaN, Infinity, and -Infinity should be considered as
204
# valid JSON and output.
209
# Returns true, if only ASCII characters should be generated. Otherwise
215
# Returns true, if quirks mode is enabled. Otherwise returns false.
220
# Configure this State instance with the Hash _opts_, and return
223
for key, value in opts
224
instance_variable_set "@#{key}", value
226
@indent = opts[:indent] if opts.key?(:indent)
227
@space = opts[:space] if opts.key?(:space)
228
@space_before = opts[:space_before] if opts.key?(:space_before)
229
@object_nl = opts[:object_nl] if opts.key?(:object_nl)
230
@array_nl = opts[:array_nl] if opts.key?(:array_nl)
231
@allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
232
@ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
233
@depth = opts[:depth] || 0
234
@quirks_mode = opts[:quirks_mode] if opts.key?(:quirks_mode)
235
@buffer_initial_length ||= opts[:buffer_initial_length]
237
if !opts.key?(:max_nesting) # defaults to 100
239
elsif opts[:max_nesting]
240
@max_nesting = opts[:max_nesting]
246
alias merge configure
248
# Returns the configuration instance variables as a hash, that can be
249
# passed to the configure method.
252
for iv in instance_variables
254
result[iv.to_sym] = self[iv]
261
# Generates a valid JSON document from object +obj+ and returns the
262
# result. If no valid JSON document can be created this method raises a
263
# GeneratorError exception.
265
result = obj.to_json(self)
267
unless result =~ /\A\s*\[/ && result =~ /\]\s*\Z/ ||
268
result =~ /\A\s*\{/ && result =~ /\}\s*\Z/
270
raise GeneratorError, "only generation of JSON objects or arrays allowed"
276
# Return the value returned by method +name+.
281
instance_variable_get("@#{name}")
286
if respond_to?(name_writer = "#{name}=")
287
__send__ name_writer, value
289
instance_variable_set "@#{name}", value
294
module GeneratorMethods
296
# Converts this object to a string (calling #to_s), converts
297
# it to a JSON string, and returns the result. This is a fallback, if no
298
# special method #to_json was defined for some object.
299
def to_json(*) to_s.to_json end
303
# Returns a JSON string containing a JSON object, that is unparsed from
304
# this Hash instance.
305
# _state_ is a JSON::State object, that can also be used to configure the
306
# produced JSON string output further.
307
# _depth_ is used to find out nesting depth, to indent accordingly.
308
def to_json(state = nil, *)
309
state = State.from_state(state)
310
state.check_max_nesting
311
json_transform(state)
316
def json_shift(state)
317
state.object_nl.empty? or return ''
318
state.indent * state.depth
321
def json_transform(state)
323
delim << state.object_nl
325
result << state.object_nl
326
depth = state.depth += 1
328
indent = !state.object_nl.empty?
330
result << delim unless first
331
result << state.indent * depth if indent
332
result << key.to_s.to_json(state)
333
result << state.space_before
335
result << state.space
336
result << value.to_json(state)
339
depth = state.depth -= 1
340
result << state.object_nl
341
result << state.indent * depth if indent if indent
348
# Returns a JSON string containing a JSON array, that is unparsed from
349
# this Array instance.
350
# _state_ is a JSON::State object, that can also be used to configure the
351
# produced JSON string output further.
352
def to_json(state = nil, *)
353
state = State.from_state(state)
354
state.check_max_nesting
355
json_transform(state)
360
def json_transform(state)
362
delim << state.array_nl
364
result << state.array_nl
365
depth = state.depth += 1
367
indent = !state.array_nl.empty?
369
result << delim unless first
370
result << state.indent * depth if indent
371
result << value.to_json(state)
374
depth = state.depth -= 1
375
result << state.array_nl
376
result << state.indent * depth if indent
382
# Returns a JSON string representation for this Integer number.
383
def to_json(*) to_s end
387
# Returns a JSON string representation for this Float number.
388
def to_json(state = nil, *)
389
state = State.from_state(state)
395
raise GeneratorError, "#{self} not allowed in JSON"
401
raise GeneratorError, "#{self} not allowed in JSON"
410
if defined?(::Encoding)
411
# This string should be encoded with UTF-8 A call to this method
412
# returns a JSON string encoded with UTF16 big endian characters as
414
def to_json(state = nil, *args)
415
state = State.from_state(state)
416
if encoding == ::Encoding::UTF_8
419
string = encode(::Encoding::UTF_8)
422
'"' << JSON.utf8_to_json_ascii(string) << '"'
424
'"' << JSON.utf8_to_json(string) << '"'
428
# This string should be encoded with UTF-8 A call to this method
429
# returns a JSON string encoded with UTF16 big endian characters as
431
def to_json(state = nil, *args)
432
state = State.from_state(state)
434
'"' << JSON.utf8_to_json_ascii(self) << '"'
436
'"' << JSON.utf8_to_json(self) << '"'
441
# Module that holds the extinding methods if, the String module is
444
# Raw Strings are JSON Objects (the raw bytes are stored in an
445
# array for the key "raw"). The Ruby String can be created by this
452
# Extends _modul_ with the String::Extend module.
453
def self.included(modul)
457
# This method creates a raw object hash, that can be nested into
458
# other data structures and will be unparsed as a raw string. This
459
# method should be used, if you want to convert raw strings to JSON
460
# instead of UTF-8 strings, e. g. binary data.
461
def to_json_raw_object
463
JSON.create_id => self.class.name,
464
'raw' => self.unpack('C*'),
468
# This method creates a JSON text from the result of
469
# a call to to_json_raw_object of this String.
470
def to_json_raw(*args)
471
to_json_raw_object.to_json(*args)
476
# Returns a JSON string for true: 'true'.
477
def to_json(*) 'true' end
481
# Returns a JSON string for false: 'false'.
482
def to_json(*) 'false' end
486
# Returns a JSON string for nil: 'null'.
487
def to_json(*) 'null' end