2 Wrapper for YAJL C library version 2.x.
5 from ctypes import Structure, c_uint, c_ubyte, c_int, c_long, c_double, \
6 c_void_p, c_char_p, CFUNCTYPE, POINTER, byref, string_at, cast , \
8 from decimal import Decimal
10 from ijson import common, backends
11 from ijson.compat import b2s
14 yajl = backends.find_yajl(2)
16 yajl.yajl_alloc.restype = POINTER(c_char)
17 yajl.yajl_get_error.restype = POINTER(c_char)
19 C_EMPTY = CFUNCTYPE(c_int, c_void_p)
20 C_INT = CFUNCTYPE(c_int, c_void_p, c_int)
21 C_LONG = CFUNCTYPE(c_int, c_void_p, c_long)
22 C_DOUBLE = CFUNCTYPE(c_int, c_void_p, c_double)
23 C_STR = CFUNCTYPE(c_int, c_void_p, POINTER(c_ubyte), c_uint)
28 Helper function casting a string that represents any Javascript number
29 into appropriate Python value: either int or Decimal.
37 # Mapping of JSON parser events to callback C types and value converters.
38 # Used to define the Callbacks structure and actual callback functions
39 # inside the parse function.
40 ('null', C_EMPTY, lambda: None),
41 ('boolean', C_INT, lambda v: bool(v)),
42 # "integer" and "double" aren't actually yielded by yajl since "number"
43 # takes precedence if defined
44 ('integer', C_LONG, lambda v, l: int(string_at(v, l))),
45 ('double', C_DOUBLE, lambda v, l: float(string_at(v, l))),
46 ('number', C_STR, lambda v, l: number(b2s(string_at(v, l)))),
47 ('string', C_STR, lambda v, l: string_at(v, l).decode('utf-8')),
48 ('start_map', C_EMPTY, lambda: None),
49 ('map_key', C_STR, lambda v, l: b2s(string_at(v, l))),
50 ('end_map', C_EMPTY, lambda: None),
51 ('start_array', C_EMPTY, lambda: None),
52 ('end_array', C_EMPTY, lambda: None),
55 class Callbacks(Structure):
56 _fields_ = [(name, type) for name, type, func in _callback_data]
60 YAJL_INSUFFICIENT_DATA = 2
63 # constants defined in yajl_parse.h
64 YAJL_ALLOW_COMMENTS = 1
65 YAJL_MULTIPLE_VALUES = 8
68 def basic_parse(f, allow_comments=False, buf_size=64 * 1024,
69 multiple_values=False):
71 Iterator yielding unprefixed events.
75 - f: a readable file-like object with JSON input
76 - allow_comments: tells parser to allow comments in JSON input
77 - buf_size: a size of an input buffer
78 - multiple_values: allows the parser to parse multiple JSON objects
82 def callback(event, func_type, func):
83 def c_callback(context, *args):
84 events.append((event, func(*args)))
86 return func_type(c_callback)
88 callbacks = Callbacks(*[callback(*data) for data in _callback_data])
89 handle = yajl.yajl_alloc(byref(callbacks), None, None)
91 yajl.yajl_config(handle, YAJL_ALLOW_COMMENTS, 1)
93 yajl.yajl_config(handle, YAJL_MULTIPLE_VALUES, 1)
96 buffer = f.read(buf_size)
98 result = yajl.yajl_parse(handle, buffer, len(buffer))
100 result = yajl.yajl_complete_parse(handle)
101 if result == YAJL_ERROR:
102 perror = yajl.yajl_get_error(handle, 1, buffer, len(buffer))
103 error = cast(perror, c_char_p).value
104 yajl.yajl_free_error(handle, perror)
105 raise common.JSONError(error)
106 if not buffer and not events:
107 if result == YAJL_INSUFFICIENT_DATA:
108 raise common.IncompleteJSONError()
115 yajl.yajl_free(handle)
117 def parse(file, **kwargs):
119 Backend-specific wrapper for ijson.common.parse.
121 return common.parse(basic_parse(file, **kwargs))
123 def items(file, prefix):
125 Backend-specific wrapper for ijson.common.items.
127 return common.items(parse(file), prefix)