Class JSON::Pure::Parser
In: lib/json/pure/parser.rb
Parent: StringScanner
JSONError GeneratorError ParserError MissingUnicodeSupport NestingError StandardError Gtk StringScanner Parser State lib/json/common.rb Ext Editor lib/json/pure/parser.rb lib/json/pure/generator.rb Object Integer FalseClass Array Hash Float NilClass TrueClass Extend String GeneratorMethods Generator Pure JSON dot/m_9_3.png

This class implements the JSON parser that is used to parse a JSON string into a Ruby data structure.

Methods

Constants

STRING = /" ((?:[^\x0-\x1f"\\] | # escaped special characters: \\["\\\/bfnrt] | \\u[0-9a-fA-F]{4} | # match all but escaped special characters: \\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*) "/nx
INTEGER = /(-?0|-?[1-9]\d*)/
FLOAT = /(-? (?:0|[1-9]\d*) (?: \.\d+(?i:e[+-]?\d+) | \.\d+ | (?i:e[+-]?\d+) ) )/x
NAN = /NaN/
INFINITY = /Infinity/
MINUS_INFINITY = /-Infinity/
OBJECT_OPEN = /\{/
OBJECT_CLOSE = /\}/
ARRAY_OPEN = /\[/
ARRAY_CLOSE = /\]/
PAIR_DELIMITER = /:/
COLLECTION_DELIMITER = /,/
TRUE = /true/
FALSE = /false/
NULL = /null/
IGNORE = %r( (?: //[^\n\r]*[\n\r]| # line comments /\* # c-style comments (?: [^*/]| # normal chars /[^*]| # slashes that do not start a nested comment \*[^/]| # asterisks that do not end this comment /(?=\*/) # single slash before this comment's end )* \*/ # the End of this comment |[ \t\r\n]+ # whitespaces: space, horicontal tab, lf, cr )+ )mx
UNPARSED = Object.new
UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }   Unescape characters in strings.

External Aliases

string -> source

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 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

[Source]

     # File lib/json/pure/parser.rb, line 71
 71:       def initialize(source, opts = {})
 72:         opts ||= {}
 73:         if defined?(::Encoding)
 74:           if source.encoding == ::Encoding::ASCII_8BIT
 75:             b = source[0, 4].bytes.to_a
 76:             source = case
 77:                      when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
 78:                        source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8)
 79:                      when b.size >= 4 && b[0] == 0 && b[2] == 0
 80:                        source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8)
 81:                      when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
 82:                        source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8)
 83: 
 84:                      when b.size >= 4 && b[1] == 0 && b[3] == 0
 85:                        source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8)
 86:                      else
 87:                        source.dup
 88:                      end
 89:           else
 90:             source = source.encode(::Encoding::UTF_8)
 91:           end
 92:           source.force_encoding(::Encoding::ASCII_8BIT)
 93:         else
 94:           b = source
 95:           source = case
 96:                    when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
 97:                      JSON.iconv('utf-8', 'utf-32be', b)
 98:                    when b.size >= 4 && b[0] == 0 && b[2] == 0
 99:                      JSON.iconv('utf-8', 'utf-16be', b)
100:                    when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
101:                      JSON.iconv('utf-8', 'utf-32le', b)
102:                    when b.size >= 4 && b[1] == 0 && b[3] == 0
103:                      JSON.iconv('utf-8', 'utf-16le', b)
104:                    else
105:                      b
106:                    end
107:         end
108:         super source
109:         if !opts.key?(:max_nesting) # defaults to 19
110:           @max_nesting = 19
111:         elsif opts[:max_nesting]
112:           @max_nesting = opts[:max_nesting]
113:         else
114:           @max_nesting = 0
115:         end
116:         @allow_nan = !!opts[:allow_nan]
117:         @symbolize_names = !!opts[:symbolize_names]
118:         ca = true
119:         ca = opts[:create_additions] if opts.key?(:create_additions)
120:         @create_id = ca ? JSON.create_id : nil
121:         @object_class = opts[:object_class] || Hash
122:         @array_class = opts[:array_class] || Array
123:       end

Public Instance methods

Parses the current JSON string source and returns the complete data structure as a result.

[Source]

     # File lib/json/pure/parser.rb, line 129
129:       def parse
130:         reset
131:         obj = nil
132:         until eos?
133:           case
134:           when scan(OBJECT_OPEN)
135:             obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
136:             @current_nesting = 1
137:             obj = parse_object
138:           when scan(ARRAY_OPEN)
139:             obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
140:             @current_nesting = 1
141:             obj = parse_array
142:           when skip(IGNORE)
143:             ;
144:           else
145:             raise ParserError, "source '#{peek(20)}' not in JSON!"
146:           end
147:         end
148:         obj or raise ParserError, "source did not contain any JSON!"
149:         obj
150:       end

Private Instance methods

[Source]

     # File lib/json/pure/parser.rb, line 230
230:       def parse_array
231:         raise NestingError, "nesting of #@current_nesting is too deep" if
232:           @max_nesting.nonzero? && @current_nesting > @max_nesting
233:         result = @array_class.new
234:         delim = false
235:         until eos?
236:           case
237:           when (value = parse_value) != UNPARSED
238:             delim = false
239:             result << value
240:             skip(IGNORE)
241:             if scan(COLLECTION_DELIMITER)
242:               delim = true
243:             elsif match?(ARRAY_CLOSE)
244:               ;
245:             else
246:               raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
247:             end
248:           when scan(ARRAY_CLOSE)
249:             if delim
250:               raise ParserError, "expected next element in array at '#{peek(20)}'!"
251:             end
252:             break
253:           when skip(IGNORE)
254:             ;
255:           else
256:             raise ParserError, "unexpected token in array at '#{peek(20)}'!"
257:           end
258:         end
259:         result
260:       end

[Source]

     # File lib/json/pure/parser.rb, line 262
262:       def parse_object
263:         raise NestingError, "nesting of #@current_nesting is too deep" if
264:           @max_nesting.nonzero? && @current_nesting > @max_nesting
265:         result = @object_class.new
266:         delim = false
267:         until eos?
268:           case
269:           when (string = parse_string) != UNPARSED
270:             skip(IGNORE)
271:             unless scan(PAIR_DELIMITER)
272:               raise ParserError, "expected ':' in object at '#{peek(20)}'!"
273:             end
274:             skip(IGNORE)
275:             unless (value = parse_value).equal? UNPARSED
276:               result[@symbolize_names ? string.to_sym : string] = value
277:               delim = false
278:               skip(IGNORE)
279:               if scan(COLLECTION_DELIMITER)
280:                 delim = true
281:               elsif match?(OBJECT_CLOSE)
282:                 ;
283:               else
284:                 raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!"
285:               end
286:             else
287:               raise ParserError, "expected value in object at '#{peek(20)}'!"
288:             end
289:           when scan(OBJECT_CLOSE)
290:             if delim
291:               raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
292:             end
293:             if @create_id and klassname = result[@create_id]
294:               klass = JSON.deep_const_get klassname
295:               break unless klass and klass.json_creatable?
296:               result = klass.json_create(result)
297:             end
298:             break
299:           when skip(IGNORE)
300:             ;
301:           else
302:             raise ParserError, "unexpected token in object at '#{peek(20)}'!"
303:           end
304:         end
305:         result
306:       end

[Source]

     # File lib/json/pure/parser.rb, line 168
168:       def parse_string
169:         if scan(STRING)
170:           return '' if self[1].empty?
171:           string = self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
172:             if u = UNESCAPE_MAP[$&[1]]
173:               u
174:             else # \uXXXX
175:               bytes = ''
176:               i = 0
177:               while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
178:                 bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
179:                 i += 1
180:               end
181:               JSON::UTF16toUTF8.iconv(bytes)
182:             end
183:           end
184:           if string.respond_to?(:force_encoding)
185:             string.force_encoding(::Encoding::UTF_8)
186:           end
187:           string
188:         else
189:           UNPARSED
190:         end
191:       rescue Iconv::Failure => e
192:         raise GeneratorError, "Caught #{e.class}: #{e}"
193:       end

[Source]

     # File lib/json/pure/parser.rb, line 195
195:       def parse_value
196:         case
197:         when scan(FLOAT)
198:           Float(self[1])
199:         when scan(INTEGER)
200:           Integer(self[1])
201:         when scan(TRUE)
202:           true
203:         when scan(FALSE)
204:           false
205:         when scan(NULL)
206:           nil
207:         when (string = parse_string) != UNPARSED
208:           string
209:         when scan(ARRAY_OPEN)
210:           @current_nesting += 1
211:           ary = parse_array
212:           @current_nesting -= 1
213:           ary
214:         when scan(OBJECT_OPEN)
215:           @current_nesting += 1
216:           obj = parse_object
217:           @current_nesting -= 1
218:           obj
219:         when @allow_nan && scan(NAN)
220:           NaN
221:         when @allow_nan && scan(INFINITY)
222:           Infinity
223:         when @allow_nan && scan(MINUS_INFINITY)
224:           MinusInfinity
225:         else
226:           UNPARSED
227:         end
228:       end

[Validate]