import json from ..base import Js indent = '' # python 3 support import six if six.PY3: basestring = str long = int xrange = range unicode = str def parse(text): reviver = arguments[1] s = text.to_string().value try: unfiltered = json.loads(s) except: raise this.MakeError('SyntaxError', 'Could not parse JSON string - Invalid syntax') unfiltered = to_js(this, unfiltered) if reviver.is_callable(): root = this.Js({'': unfiltered}) return walk(root, '', reviver) else: return unfiltered def stringify(value, replacer, space): global indent stack = set([]) indent = '' property_list = replacer_function = this.undefined if replacer.is_object(): if replacer.is_callable(): replacer_function = replacer elif replacer.Class == 'Array': property_list = [] for e in replacer: v = replacer[e] item = this.undefined if v._type() == 'Number': item = v.to_string() elif v._type() == 'String': item = v elif v.is_object(): if v.Class in ('String', 'Number'): item = v.to_string() if not item.is_undefined() and item.value not in property_list: property_list.append(item.value) if space.is_object(): if space.Class == 'Number': space = space.to_number() elif space.Class == 'String': space = space.to_string() if space._type() == 'Number': space = this.Js(min(10, space.to_int())) gap = max(int(space.value), 0) * ' ' elif space._type() == 'String': gap = space.value[:10] else: gap = '' return this.Js( Str('', this.Js({ '': value }), replacer_function, property_list, gap, stack, space)) def Str(key, holder, replacer_function, property_list, gap, stack, space): value = holder[key] if value.is_object(): to_json = value.get('toJSON') if to_json.is_callable(): value = to_json.call(value, (key, )) if not replacer_function.is_undefined(): value = replacer_function.call(holder, (key, value)) if value.is_object(): if value.Class == 'String': value = value.to_string() elif value.Class == 'Number': value = value.to_number() elif value.Class == 'Boolean': value = value.to_boolean() if value.is_null(): return 'null' elif value.Class == 'Boolean': return 'true' if value.value else 'false' elif value._type() == 'String': return Quote(value) elif value._type() == 'Number': if not value.is_infinity(): return value.to_string() return 'null' if value.is_object() and not value.is_callable(): if value.Class == 'Array': return ja(value, stack, gap, property_list, replacer_function, space) else: return jo(value, stack, gap, property_list, replacer_function, space) return None # undefined def jo(value, stack, gap, property_list, replacer_function, space): global indent if value in stack: raise value.MakeError('TypeError', 'Converting circular structure to JSON') stack.add(value) stepback = indent indent += gap if not property_list.is_undefined(): k = property_list else: k = [e.value for e in value] partial = [] for p in k: str_p = value.Js( Str(p, value, replacer_function, property_list, gap, stack, space)) if not str_p.is_undefined(): member = json.dumps(p) + ':' + ( ' ' if gap else '') + str_p.value # todo not sure here - what space character? partial.append(member) if not partial: final = '{}' else: if not gap: final = '{%s}' % ','.join(partial) else: sep = ',\n' + indent properties = sep.join(partial) final = '{\n' + indent + properties + '\n' + stepback + '}' stack.remove(value) indent = stepback return final def ja(value, stack, gap, property_list, replacer_function, space): global indent if value in stack: raise value.MakeError('TypeError', 'Converting circular structure to JSON') stack.add(value) stepback = indent indent += gap partial = [] length = len(value) for index in xrange(length): index = str(index) str_index = value.Js( Str(index, value, replacer_function, property_list, gap, stack, space)) if str_index.is_undefined(): partial.append('null') else: partial.append(str_index.value) if not partial: final = '[]' else: if not gap: final = '[%s]' % ','.join(partial) else: sep = ',\n' + indent properties = sep.join(partial) final = '[\n' + indent + properties + '\n' + stepback + ']' stack.remove(value) indent = stepback return final def Quote(string): return string.Js(json.dumps(string.value)) def to_js(this, d): if isinstance(d, dict): return this.Js(dict((k, this.Js(v)) for k, v in six.iteritems(d))) return this.Js(d) def walk(holder, name, reviver): val = holder.get(name) if val.Class == 'Array': for i in xrange(len(val)): i = unicode(i) new_element = walk(val, i, reviver) if new_element.is_undefined(): val.delete(i) else: new_element.put(i, new_element) elif val.is_object(): for key in val: new_element = walk(val, key, reviver) if new_element.is_undefined(): val.delete(key) else: val.put(key, new_element) return reviver.call(holder, (name, val)) JSON = Js({}) JSON.define_own_property( 'parse', { 'value': Js(parse), 'enumerable': False, 'writable': True, 'configurable': True }) JSON.define_own_property( 'stringify', { 'value': Js(stringify), 'enumerable': False, 'writable': True, 'configurable': True })