class JSON::Pure::Parser
into a Ruby data structure.
This class implements the JSON parser that is used to parse a JSON string
def convert_encoding(source)
def convert_encoding(source) if source.respond_to?(:to_str) source = source.to_str else raise TypeError, "#{source.inspect} is not like a string" end if source.encoding != ::Encoding::ASCII_8BIT source = source.encode(::Encoding::UTF_8) source.force_encoding(::Encoding::ASCII_8BIT) end source end
def initialize(source, opts = {})
(Float) when parsing decimal numbers. This class must accept a single
* *decimal_class*: Specifies which class to use instead of the default
* *array_class*: Defaults to Array
* *object_class*: Defaults to Hash
option defaults to false.
additions when a matching class and create_id are found. This
* *create_additions*: If set to true, the Parser creates
conjunction with the *create_additions* option.
also the default. It's not possible to use this option in
(keys) in a JSON object. Otherwise strings are returned, which is
* *symbolize_names*: If set to true, returns symbols for the names
string will be deduplicated if possible.
* *freeze*: If set to true, all parsed objects will be frozen. Parsed
to false.
defiance of RFC 7159 to be parsed by the Parser. This option defaults
* *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
it defaults to 100.
structures. Disable depth checking with :max_nesting => false|nil|0,
* *max_nesting*: The maximum depth of nesting allowed in the parsed data
keys:
It will be configured by the _opts_ hash. _opts_ can have the following
Creates a new JSON::Pure::Parser instance for the string _source_.
def initialize(source, opts = {}) opts ||= {} source = convert_encoding source super source if !opts.key?(:max_nesting) # defaults to 100 @max_nesting = 100 elsif opts[:max_nesting] @max_nesting = opts[:max_nesting] else @max_nesting = 0 end @allow_nan = !!opts[:allow_nan] @symbolize_names = !!opts[:symbolize_names] @freeze = !!opts[:freeze] if opts.key?(:create_additions) @create_additions = !!opts[:create_additions] else @create_additions = false end @symbolize_names && @create_additions and raise ArgumentError, 'options :symbolize_names and :create_additions cannot be used '\ 'in conjunction' @create_id = @create_additions ? JSON.create_id : nil @object_class = opts[:object_class] || Hash @array_class = opts[:array_class] || Array @decimal_class = opts[:decimal_class] @match_string = opts[:match_string] end
def parse
Parses the current JSON string _source_ and returns the
def parse reset obj = nil while !eos? && skip(IGNORE) do end if eos? raise ParserError, "source is not valid JSON!" else obj = parse_value UNPARSED.equal?(obj) and raise ParserError, "source is not valid JSON!" obj.freeze if @freeze end while !eos? && skip(IGNORE) do end eos? or raise ParserError, "source is not valid JSON!" obj end
def parse_array
def parse_array raise NestingError, "nesting of #@current_nesting is too deep" if @max_nesting.nonzero? && @current_nesting > @max_nesting result = @array_class.new delim = false loop do case when eos? raise ParserError, "unexpected end of string while parsing array" when !UNPARSED.equal?(value = parse_value) delim = false result << value skip(IGNORE) if scan(COLLECTION_DELIMITER) delim = true elsif match?(ARRAY_CLOSE) ; else raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!" end when scan(ARRAY_CLOSE) if delim raise ParserError, "expected next element in array at '#{peek(20)}'!" end break when skip(IGNORE) ; else raise ParserError, "unexpected token in array at '#{peek(20)}'!" end end result end
def parse_object
def parse_object raise NestingError, "nesting of #@current_nesting is too deep" if @max_nesting.nonzero? && @current_nesting > @max_nesting result = @object_class.new delim = false loop do case when eos? raise ParserError, "unexpected end of string while parsing object" when !UNPARSED.equal?(string = parse_string) skip(IGNORE) unless scan(PAIR_DELIMITER) raise ParserError, "expected ':' in object at '#{peek(20)}'!" end skip(IGNORE) unless UNPARSED.equal?(value = parse_value) result[@symbolize_names ? string.to_sym : string] = value delim = false skip(IGNORE) if scan(COLLECTION_DELIMITER) delim = true elsif match?(OBJECT_CLOSE) ; else raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!" end else raise ParserError, "expected value in object at '#{peek(20)}'!" end when scan(OBJECT_CLOSE) if delim raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!" end if @create_additions and klassname = result[@create_id] klass = JSON.deep_const_get klassname break unless klass and klass.json_creatable? result = klass.json_create(result) end break when skip(IGNORE) ; else raise ParserError, "unexpected token in object at '#{peek(20)}'!" end end result end
def parse_string
def parse_string if scan(STRING) return '' if self[1].empty? string = self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c| if u = UNESCAPE_MAP[$&[1]] u else # \uXXXX bytes = EMPTY_8BIT_STRING.dup i = 0 while c[6 * i] == ?\\ && c[6 * i + 1] == ?u bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16) i += 1 end JSON.iconv('utf-8', 'utf-16be', bytes).force_encoding(::Encoding::ASCII_8BIT) end end if string.respond_to?(:force_encoding) string.force_encoding(::Encoding::UTF_8) end if @freeze if STR_UMINUS string = -string else string.freeze end end if @create_additions and @match_string for (regexp, klass) in @match_string klass.json_creatable? or next string =~ regexp and return klass.json_create(string) end end string else UNPARSED end rescue => e raise ParserError, "Caught #{e.class} at '#{peek(20)}': #{e}" end
def parse_value
def parse_value case when scan(FLOAT) if @decimal_class then if @decimal_class == BigDecimal then BigDecimal(self[1]) else @decimal_class.new(self[1]) || Float(self[1]) end else Float(self[1]) end when scan(INTEGER) Integer(self[1]) when scan(TRUE) true when scan(FALSE) false when scan(NULL) nil when !UNPARSED.equal?(string = parse_string) string when scan(ARRAY_OPEN) @current_nesting += 1 ary = parse_array @current_nesting -= 1 ary when scan(OBJECT_OPEN) @current_nesting += 1 obj = parse_object @current_nesting -= 1 obj when @allow_nan && scan(NAN) NaN when @allow_nan && scan(INFINITY) Infinity when @allow_nan && scan(MINUS_INFINITY) MinusInfinity else UNPARSED end end
def reset
def reset super @current_nesting = 0 end