1""" 2raven.utils.json 3~~~~~~~~~~~~~~~~~~~~~~~~ 4 5:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details. 6:license: BSD, see LICENSE for more details. 7""" 8 9from __future__ import absolute_import 10 11import codecs 12import datetime 13import uuid 14import json 15 16try: 17 JSONDecodeError = json.JSONDecodeError 18except AttributeError: 19 JSONDecodeError = ValueError 20 21 22class BetterJSONEncoder(json.JSONEncoder): 23 ENCODER_BY_TYPE = { 24 uuid.UUID: lambda o: o.hex, 25 datetime.datetime: lambda o: o.strftime('%Y-%m-%dT%H:%M:%SZ'), 26 set: list, 27 frozenset: list, 28 bytes: lambda o: o.decode('utf-8', errors='replace') 29 } 30 31 def default(self, obj): 32 try: 33 encoder = self.ENCODER_BY_TYPE[type(obj)] 34 except KeyError: 35 try: 36 return super(BetterJSONEncoder, self).default(obj) 37 except TypeError: 38 return repr(obj) 39 return encoder(obj) 40 41 42def better_decoder(data): 43 return data 44 45 46def dumps(value, **kwargs): 47 try: 48 return json.dumps(value, cls=BetterJSONEncoder, **kwargs) 49 except Exception: 50 kwargs['encoding'] = 'safe-utf-8' 51 return json.dumps(value, cls=BetterJSONEncoder, **kwargs) 52 53 54def loads(value, **kwargs): 55 return json.loads(value, object_hook=better_decoder) 56 57 58_utf8_encoder = codecs.getencoder('utf-8') 59 60 61def safe_encode(input, errors='backslashreplace'): 62 return _utf8_encoder(input, errors) 63 64 65_utf8_decoder = codecs.getdecoder('utf-8') 66 67 68def safe_decode(input, errors='replace'): 69 return _utf8_decoder(input, errors) 70 71 72class Codec(codecs.Codec): 73 74 def encode(self, input, errors='backslashreplace'): 75 return safe_encode(input, errors) 76 77 def decode(self, input, errors='replace'): 78 return safe_decode(input, errors) 79 80 81class IncrementalEncoder(codecs.IncrementalEncoder): 82 def encode(self, input, final=False): 83 return safe_encode(input, self.errors)[0] 84 85 86class IncrementalDecoder(codecs.IncrementalDecoder): 87 def decode(self, input, final=False): 88 return safe_decode(input, self.errors)[0] 89 90 91class StreamWriter(Codec, codecs.StreamWriter): 92 pass 93 94 95class StreamReader(Codec, codecs.StreamReader): 96 pass 97 98 99def getregentry(name): 100 if name == 'safe-utf-8': 101 return codecs.CodecInfo( 102 name=name, 103 encode=safe_encode, 104 decode=safe_decode, 105 incrementalencoder=IncrementalEncoder, 106 incrementaldecoder=IncrementalDecoder, 107 streamreader=StreamReader, 108 streamwriter=StreamWriter, 109 ) 110 111 112codecs.register(getregentry) 113