StringScanner
This class implements the JSON parser that is used to parse a JSON string into a Ruby data structure.
Unescape characters in strings.
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 19.
allow_nan: If set to true, allow NaN, Infinity and -Infinity in defiance of RFC 4627 to be parsed by the Parser. This option defaults to false.
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.
create_additions: If set to false, the Parser doesn’t create additions even if a matchin class and create_id was found. This option defaults to true.
object_class: Defaults to Hash
array_class: Defaults to Array
quirks_mode: Enables quirks_mode for parser, that is for example parsing single JSON values instead of documents is possible.
# File lib/json/pure/parser.rb, line 73 73: def initialize(source, opts = {}) 74: opts ||= {} 75: unless @quirks_mode = opts[:quirks_mode] 76: source = convert_encoding source 77: end 78: super source 79: if !opts.key?(:max_nesting) # defaults to 19 80: @max_nesting = 19 81: elsif opts[:max_nesting] 82: @max_nesting = opts[:max_nesting] 83: else 84: @max_nesting = 0 85: end 86: @allow_nan = !!opts[:allow_nan] 87: @symbolize_names = !!opts[:symbolize_names] 88: if opts.key?(:create_additions) 89: @create_additions = !!opts[:create_additions] 90: else 91: @create_additions = true 92: end 93: @create_id = @create_additions ? JSON.create_id : nil 94: @object_class = opts[:object_class] || Hash 95: @array_class = opts[:array_class] || Array 96: @match_string = opts[:match_string] 97: end
Parses the current JSON string source and returns the complete data structure as a result.
# File lib/json/pure/parser.rb, line 112 112: def parse 113: reset 114: obj = nil 115: if @quirks_mode 116: while !eos? && skip(IGNORE) 117: end 118: if eos? 119: raise ParserError, "source did not contain any JSON!" 120: else 121: obj = parse_value 122: obj == UNPARSED and raise ParserError, "source did not contain any JSON!" 123: end 124: else 125: until eos? 126: case 127: when scan(OBJECT_OPEN) 128: obj and raise ParserError, "source '#{peek(20)}' not in JSON!" 129: @current_nesting = 1 130: obj = parse_object 131: when scan(ARRAY_OPEN) 132: obj and raise ParserError, "source '#{peek(20)}' not in JSON!" 133: @current_nesting = 1 134: obj = parse_array 135: when skip(IGNORE) 136: ; 137: else 138: raise ParserError, "source '#{peek(20)}' not in JSON!" 139: end 140: end 141: obj or raise ParserError, "source did not contain any JSON!" 142: end 143: obj 144: end
# File lib/json/pure/parser.rb, line 148 148: def convert_encoding(source) 149: if source.respond_to?(:to_str) 150: source = source.to_str 151: else 152: raise TypeError, "#{source.inspect} is not like a string" 153: end 154: if defined?(::Encoding) 155: if source.encoding == ::Encoding::ASCII_8BIT 156: b = source[0, 4].bytes.to_a 157: source = 158: case 159: when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 160: source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8) 161: when b.size >= 4 && b[0] == 0 && b[2] == 0 162: source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8) 163: when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 164: source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8) 165: when b.size >= 4 && b[1] == 0 && b[3] == 0 166: source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8) 167: else 168: source.dup 169: end 170: else 171: source = source.encode(::Encoding::UTF_8) 172: end 173: source.force_encoding(::Encoding::ASCII_8BIT) 174: else 175: b = source 176: source = 177: case 178: when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 179: JSON.iconv('utf-8', 'utf-32be', b) 180: when b.size >= 4 && b[0] == 0 && b[2] == 0 181: JSON.iconv('utf-8', 'utf-16be', b) 182: when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 183: JSON.iconv('utf-8', 'utf-32le', b) 184: when b.size >= 4 && b[1] == 0 && b[3] == 0 185: JSON.iconv('utf-8', 'utf-16le', b) 186: else 187: b 188: end 189: end 190: source 191: end
# File lib/json/pure/parser.rb, line 280 280: def parse_array 281: raise NestingError, "nesting of #@current_nesting is too deep" if 282: @max_nesting.nonzero? && @current_nesting > @max_nesting 283: result = @array_class.new 284: delim = false 285: until eos? 286: case 287: when (value = parse_value) != UNPARSED 288: delim = false 289: result << value 290: skip(IGNORE) 291: if scan(COLLECTION_DELIMITER) 292: delim = true 293: elsif match?(ARRAY_CLOSE) 294: ; 295: else 296: raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!" 297: end 298: when scan(ARRAY_CLOSE) 299: if delim 300: raise ParserError, "expected next element in array at '#{peek(20)}'!" 301: end 302: break 303: when skip(IGNORE) 304: ; 305: else 306: raise ParserError, "unexpected token in array at '#{peek(20)}'!" 307: end 308: end 309: result 310: end
# File lib/json/pure/parser.rb, line 312 312: def parse_object 313: raise NestingError, "nesting of #@current_nesting is too deep" if 314: @max_nesting.nonzero? && @current_nesting > @max_nesting 315: result = @object_class.new 316: delim = false 317: until eos? 318: case 319: when (string = parse_string) != UNPARSED 320: skip(IGNORE) 321: unless scan(PAIR_DELIMITER) 322: raise ParserError, "expected ':' in object at '#{peek(20)}'!" 323: end 324: skip(IGNORE) 325: unless (value = parse_value).equal? UNPARSED 326: result[@symbolize_names ? string.to_sym : string] = value 327: delim = false 328: skip(IGNORE) 329: if scan(COLLECTION_DELIMITER) 330: delim = true 331: elsif match?(OBJECT_CLOSE) 332: ; 333: else 334: raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!" 335: end 336: else 337: raise ParserError, "expected value in object at '#{peek(20)}'!" 338: end 339: when scan(OBJECT_CLOSE) 340: if delim 341: raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!" 342: end 343: if @create_additions and klassname = result[@create_id] 344: klass = JSON.deep_const_get klassname 345: break unless klass and klass.json_creatable? 346: result = klass.json_create(result) 347: end 348: break 349: when skip(IGNORE) 350: ; 351: else 352: raise ParserError, "unexpected token in object at '#{peek(20)}'!" 353: end 354: end 355: result 356: end
# File lib/json/pure/parser.rb, line 212 212: def parse_string 213: if scan(STRING) 214: return '' if self[1].empty? 215: string = self[1].gsub(%((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))) do |c| 216: if u = UNESCAPE_MAP[$&[1]] 217: u 218: else # \uXXXX 219: bytes = EMPTY_8BIT_STRING.dup 220: i = 0 221: while c[6 * i] == \\\ && c[6 * i + 1] == uu 222: bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16) 223: i += 1 224: end 225: JSON.iconv('utf-8', 'utf-16be', bytes) 226: end 227: end 228: if string.respond_to?(:force_encoding) 229: string.force_encoding(::Encoding::UTF_8) 230: end 231: if @create_additions and @match_string 232: for (regexp, klass) in @match_string 233: klass.json_creatable? or next 234: string =~ regexp and return klass.json_create(string) 235: end 236: end 237: string 238: else 239: UNPARSED 240: end 241: rescue => e 242: raise ParserError, "Caught #{e.class} at '#{peek(20)}': #{e}" 243: end
# File lib/json/pure/parser.rb, line 245 245: def parse_value 246: case 247: when scan(FLOAT) 248: Float(self[1]) 249: when scan(INTEGER) 250: Integer(self[1]) 251: when scan(TRUE) 252: true 253: when scan(FALSE) 254: false 255: when scan(NULL) 256: nil 257: when (string = parse_string) != UNPARSED 258: string 259: when scan(ARRAY_OPEN) 260: @current_nesting += 1 261: ary = parse_array 262: @current_nesting -= 1 263: ary 264: when scan(OBJECT_OPEN) 265: @current_nesting += 1 266: obj = parse_object 267: @current_nesting -= 1 268: obj 269: when @allow_nan && scan(NAN) 270: NaN 271: when @allow_nan && scan(INFINITY) 272: Infinity 273: when @allow_nan && scan(MINUS_INFINITY) 274: MinusInfinity 275: else 276: UNPARSED 277: end 278: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.