Class JSON::Pure::Parser
In: lib/json/pure/parser.rb
Parent: StringScanner
JSONError GeneratorError ParserError MissingUnicodeSupport CircularDatastructure 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.
  • 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 68
68:       def initialize(source, opts = {})
69:         super
70:         if !opts.key?(:max_nesting) # defaults to 19
71:           @max_nesting = 19
72:         elsif opts[:max_nesting]
73:           @max_nesting = opts[:max_nesting]
74:         else
75:           @max_nesting = 0
76:         end
77:         @allow_nan = !!opts[:allow_nan]
78:         ca = true
79:         ca = opts[:create_additions] if opts.key?(:create_additions)
80:         @create_id = ca ? JSON.create_id : nil
81:         @object_class = opts[:object_class] || Hash
82:         @array_class = opts[:array_class] || Array
83:       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 89
 89:       def parse
 90:         reset
 91:         obj = nil
 92:         until eos?
 93:           case
 94:           when scan(OBJECT_OPEN)
 95:             obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
 96:             @current_nesting = 1
 97:             obj = parse_object
 98:           when scan(ARRAY_OPEN)
 99:             obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
100:             @current_nesting = 1
101:             obj = parse_array
102:           when skip(IGNORE)
103:             ;
104:           else
105:             raise ParserError, "source '#{peek(20)}' not in JSON!"
106:           end
107:         end
108:         obj or raise ParserError, "source did not contain any JSON!"
109:         obj
110:       end

Private Instance methods

[Source]

     # File lib/json/pure/parser.rb, line 190
190:       def parse_array
191:         raise NestingError, "nesting of #@current_nesting is too deep" if
192:           @max_nesting.nonzero? && @current_nesting > @max_nesting
193:         result = @array_class.new
194:         delim = false
195:         until eos?
196:           case
197:           when (value = parse_value) != UNPARSED
198:             delim = false
199:             result << value
200:             skip(IGNORE)
201:             if scan(COLLECTION_DELIMITER)
202:               delim = true
203:             elsif match?(ARRAY_CLOSE)
204:               ;
205:             else
206:               raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
207:             end
208:           when scan(ARRAY_CLOSE)
209:             if delim
210:               raise ParserError, "expected next element in array at '#{peek(20)}'!"
211:             end
212:             break
213:           when skip(IGNORE)
214:             ;
215:           else
216:             raise ParserError, "unexpected token in array at '#{peek(20)}'!"
217:           end
218:         end
219:         result
220:       end

[Source]

     # File lib/json/pure/parser.rb, line 222
222:       def parse_object
223:         raise NestingError, "nesting of #@current_nesting is too deep" if
224:           @max_nesting.nonzero? && @current_nesting > @max_nesting
225:         result = @object_class.new
226:         delim = false
227:         until eos?
228:           case
229:           when (string = parse_string) != UNPARSED
230:             skip(IGNORE)
231:             unless scan(PAIR_DELIMITER)
232:               raise ParserError, "expected ':' in object at '#{peek(20)}'!"
233:             end
234:             skip(IGNORE)
235:             unless (value = parse_value).equal? UNPARSED
236:               result[string] = value
237:               delim = false
238:               skip(IGNORE)
239:               if scan(COLLECTION_DELIMITER)
240:                 delim = true
241:               elsif match?(OBJECT_CLOSE)
242:                 ;
243:               else
244:                 raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!"
245:               end
246:             else
247:               raise ParserError, "expected value in object at '#{peek(20)}'!"
248:             end
249:           when scan(OBJECT_CLOSE)
250:             if delim
251:               raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
252:             end
253:             if @create_id and klassname = result[@create_id]
254:               klass = JSON.deep_const_get klassname
255:               break unless klass and klass.json_creatable?
256:               result = klass.json_create(result)
257:             end
258:             break
259:           when skip(IGNORE)
260:             ;
261:           else
262:             raise ParserError, "unexpected token in object at '#{peek(20)}'!"
263:           end
264:         end
265:         result
266:       end

[Source]

     # File lib/json/pure/parser.rb, line 128
128:       def parse_string
129:         if scan(STRING)
130:           return '' if self[1].empty?
131:           string = self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
132:             if u = UNESCAPE_MAP[$&[1]]
133:               u
134:             else # \uXXXX
135:               bytes = ''
136:               i = 0
137:               while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
138:                 bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
139:                 i += 1
140:               end
141:               JSON::UTF16toUTF8.iconv(bytes)
142:             end
143:           end
144:           if string.respond_to?(:force_encoding)
145:             string.force_encoding(Encoding::UTF_8)
146:           end
147:           string
148:         else
149:           UNPARSED
150:         end
151:       rescue Iconv::Failure => e
152:         raise GeneratorError, "Caught #{e.class}: #{e}"
153:       end

[Source]

     # File lib/json/pure/parser.rb, line 155
155:       def parse_value
156:         case
157:         when scan(FLOAT)
158:           Float(self[1])
159:         when scan(INTEGER)
160:           Integer(self[1])
161:         when scan(TRUE)
162:           true
163:         when scan(FALSE)
164:           false
165:         when scan(NULL)
166:           nil
167:         when (string = parse_string) != UNPARSED
168:           string
169:         when scan(ARRAY_OPEN)
170:           @current_nesting += 1
171:           ary = parse_array
172:           @current_nesting -= 1
173:           ary
174:         when scan(OBJECT_OPEN)
175:           @current_nesting += 1
176:           obj = parse_object
177:           @current_nesting -= 1
178:           obj
179:         when @allow_nan && scan(NAN)
180:           NaN
181:         when @allow_nan && scan(INFINITY)
182:           Infinity
183:         when @allow_nan && scan(MINUS_INFINITY)
184:           MinusInfinity
185:         else
186:           UNPARSED
187:         end
188:       end

[Validate]