class JSON::Pure::Parser
This class implements the JSON
parser that is used to parse a JSON
string into a Ruby data structure.
Constants
- ARRAY_CLOSE
- ARRAY_OPEN
- COLLECTION_DELIMITER
- EMPTY_8BIT_STRING
- FALSE
- FLOAT
- IGNORE
- INFINITY
- INTEGER
- MINUS_INFINITY
- NAN
- NULL
- OBJECT_CLOSE
- OBJECT_OPEN
- PAIR_DELIMITER
- STRING
- STR_UMINUS
- TRUE
- UNESCAPE_MAP
Unescape characters in strings.
- UNPARSED
Public Class Methods
Creates a new JSON::Pure::Parser
instance for the string source.
It will be configured by the opts hash. opts can have the following keys:
-
max_nesting: The maximum depth of nesting allowed in the parsed data structures. Disable depth checking with :max_nesting => false|nil|0, it defaults to 100.
-
allow_nan: If set to true, allow NaN, Infinity and -Infinity in defiance of RFC 7159 to be parsed by the
Parser
. This option defaults to false. -
freeze: If set to true, all parsed objects will be frozen. Parsed string will be deduplicated if possible.
-
symbolize_names: If set to true, returns symbols for the names (keys) in a
JSON
object. Otherwise strings are returned, which is also the default. It’s not possible to use this option in conjunction with the create_additions option. -
create_additions: If set to true, the
Parser
creates additions when a matching class and create_id are found. This option defaults to false. -
object_class: Defaults to Hash
-
array_class: Defaults to Array
-
decimal_class: Specifies which class to use instead of the default
(Float) when parsing decimal numbers. This class must accept a single string argument in its constructor.
# File lib/json/pure/parser.rb, line 78 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
Public Instance Methods
Parses the current JSON
string source and returns the complete data structure as a result.
# File lib/json/pure/parser.rb, line 116 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
# File lib/json/pure/parser.rb, line 109 def reset super @current_nesting = 0 end
Private Instance Methods
# File lib/json/pure/parser.rb, line 135 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
# File lib/json/pure/parser.rb, line 254 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
# File lib/json/pure/parser.rb, line 288 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
# File lib/json/pure/parser.rb, line 169 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
# File lib/json/pure/parser.rb, line 211 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