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 << '' # XXX workaround: avoid buffer sharing
45 string.force_encoding(::Encoding::ASCII_8BIT)
46 string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
47 string.force_encoding(::Encoding::UTF_8)
51 def utf8_to_json_ascii(string) # :nodoc:
53 string << '' # XXX workaround: avoid buffer sharing
54 string.force_encoding(::Encoding::ASCII_8BIT)
55 string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
58 [\xc2-\xdf][\x80-\xbf] |
59 [\xe0-\xef][\x80-\xbf]{2} |
60 [\xf0-\xf4][\x80-\xbf]{3}
62 [\x80-\xc1\xf5-\xff] # invalid
64 c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
65 s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
66 s.gsub!(/.{4}/n, '\\\\u\&')
68 string.force_encoding(::Encoding::UTF_8)
71 raise GeneratorError, "Caught #{e.class}: #{e}"
74 def utf8_to_json(string) # :nodoc:
75 string.gsub(/["\\\x0-\x1f]/) { 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, "Caught #{e.class}: #{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 = {})
150 # This string is used to indent levels in the JSON text.
151 attr_accessor :indent
153 # This string is used to insert a space between the tokens in a JSON
157 # This string is used to insert a space before the ':' in JSON objects.
158 attr_accessor :space_before
160 # This string is put at the end of a line that holds a JSON object (or
162 attr_accessor :object_nl
164 # This string is put at the end of a line that holds a JSON array.
165 attr_accessor :array_nl
167 # This integer returns the maximum level of data structure nesting in
168 # the generated JSON, max_nesting = 0 if no maximum is checked.
169 attr_accessor :max_nesting
171 # If this attribute is set to true, quirks mode is enabled, otherwise
173 attr_accessor :quirks_mode
175 # This integer returns the current depth data structure nesting in the
179 def check_max_nesting # :nodoc:
180 return if @max_nesting.zero?
181 current_nesting = depth + 1
182 current_nesting > @max_nesting and
183 raise NestingError, "nesting of #{current_nesting} is too deep"
186 # Returns true, if circular data structures are checked,
187 # otherwise returns false.
192 # Returns true if NaN, Infinity, and -Infinity should be considered as
193 # valid JSON and output.
198 # Returns true, if only ASCII characters should be generated. Otherwise
204 # Returns true, if quirks mode is enabled. Otherwise returns false.
209 # Configure this State instance with the Hash _opts_, and return
212 @indent = opts[:indent] if opts.key?(:indent)
213 @space = opts[:space] if opts.key?(:space)
214 @space_before = opts[:space_before] if opts.key?(:space_before)
215 @object_nl = opts[:object_nl] if opts.key?(:object_nl)
216 @array_nl = opts[:array_nl] if opts.key?(:array_nl)
217 @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
218 @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
219 @depth = opts[:depth] || 0
220 @quirks_mode = opts[:quirks_mode] if opts.key?(:quirks_mode)
221 if !opts.key?(:max_nesting) # defaults to 19
223 elsif opts[:max_nesting]
224 @max_nesting = opts[:max_nesting]
230 alias merge configure
232 # Returns the configuration instance variables as a hash, that can be
233 # passed to the configure method.
236 for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only quirks_mode depth]
237 result[iv.intern] = instance_variable_get("@#{iv}")
242 # Generates a valid JSON document from object +obj+ and returns the
243 # result. If no valid JSON document can be created this method raises a
244 # GeneratorError exception.
246 result = obj.to_json(self)
247 if !@quirks_mode && result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m
248 raise GeneratorError, "only generation of JSON objects or arrays allowed"
253 # Return the value returned by method +name+.
259 module GeneratorMethods
261 # Converts this object to a string (calling #to_s), converts
262 # it to a JSON string, and returns the result. This is a fallback, if no
263 # special method #to_json was defined for some object.
264 def to_json(*) to_s.to_json end
268 # Returns a JSON string containing a JSON object, that is unparsed from
269 # this Hash instance.
270 # _state_ is a JSON::State object, that can also be used to configure the
271 # produced JSON string output further.
272 # _depth_ is used to find out nesting depth, to indent accordingly.
273 def to_json(state = nil, *)
274 state = State.from_state(state)
275 state.check_max_nesting
276 json_transform(state)
281 def json_shift(state)
282 state.object_nl.empty? or return ''
283 state.indent * state.depth
286 def json_transform(state)
288 delim << state.object_nl
290 result << state.object_nl
291 depth = state.depth += 1
293 indent = !state.object_nl.empty?
295 result << delim unless first
296 result << state.indent * depth if indent
297 result << key.to_s.to_json(state)
298 result << state.space_before
300 result << state.space
301 result << value.to_json(state)
304 depth = state.depth -= 1
305 result << state.object_nl
306 result << state.indent * depth if indent if indent
313 # Returns a JSON string containing a JSON array, that is unparsed from
314 # this Array instance.
315 # _state_ is a JSON::State object, that can also be used to configure the
316 # produced JSON string output further.
317 def to_json(state = nil, *)
318 state = State.from_state(state)
319 state.check_max_nesting
320 json_transform(state)
325 def json_transform(state)
327 delim << state.array_nl
329 result << state.array_nl
330 depth = state.depth += 1
332 indent = !state.array_nl.empty?
334 result << delim unless first
335 result << state.indent * depth if indent
336 result << value.to_json(state)
339 depth = state.depth -= 1
340 result << state.array_nl
341 result << state.indent * depth if indent
347 # Returns a JSON string representation for this Integer number.
348 def to_json(*) to_s end
352 # Returns a JSON string representation for this Float number.
353 def to_json(state = nil, *)
354 state = State.from_state(state)
360 raise GeneratorError, "#{self} not allowed in JSON"
366 raise GeneratorError, "#{self} not allowed in JSON"
375 if defined?(::Encoding)
376 # This string should be encoded with UTF-8 A call to this method
377 # returns a JSON string encoded with UTF16 big endian characters as
379 def to_json(state = nil, *args)
380 state = State.from_state(state)
381 if encoding == ::Encoding::UTF_8
384 string = encode(::Encoding::UTF_8)
387 '"' << JSON.utf8_to_json_ascii(string) << '"'
389 '"' << JSON.utf8_to_json(string) << '"'
393 # This string should be encoded with UTF-8 A call to this method
394 # returns a JSON string encoded with UTF16 big endian characters as
396 def to_json(state = nil, *args)
397 state = State.from_state(state)
399 '"' << JSON.utf8_to_json_ascii(self) << '"'
401 '"' << JSON.utf8_to_json(self) << '"'
406 # Module that holds the extinding methods if, the String module is
409 # Raw Strings are JSON Objects (the raw bytes are stored in an
410 # array for the key "raw"). The Ruby String can be created by this
417 # Extends _modul_ with the String::Extend module.
418 def self.included(modul)
422 # This method creates a raw object hash, that can be nested into
423 # other data structures and will be unparsed as a raw string. This
424 # method should be used, if you want to convert raw strings to JSON
425 # instead of UTF-8 strings, e. g. binary data.
426 def to_json_raw_object
428 JSON.create_id => self.class.name,
429 'raw' => self.unpack('C*'),
433 # This method creates a JSON text from the result of
434 # a call to to_json_raw_object of this String.
435 def to_json_raw(*args)
436 to_json_raw_object.to_json(*args)
441 # Returns a JSON string for true: 'true'.
442 def to_json(*) 'true' end
446 # Returns a JSON string for false: 'false'.
447 def to_json(*) 'false' end
451 # Returns a JSON string for nil: 'null'.
452 def to_json(*) 'null' end