Allow to register different type for json/xmlrpc encoding/decoding
issue3839 review5021003
This commit is contained in:
@@ -7,7 +7,7 @@ except ImportError:
|
||||
from sql import Query, Expression
|
||||
|
||||
from .field import Field, SQLType
|
||||
from ...protocols.jsonrpc import object_hook, JSONEncoder
|
||||
from ...protocols.jsonrpc import JSONDecoder, JSONEncoder
|
||||
|
||||
|
||||
class Dict(Field):
|
||||
@@ -28,7 +28,7 @@ class Dict(Field):
|
||||
for value in values or []:
|
||||
if value[name]:
|
||||
dicts[value['id']] = json.loads(value[name],
|
||||
object_hook=object_hook)
|
||||
object_hook=JSONDecoder())
|
||||
return dicts
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -33,63 +33,86 @@ except ImportError:
|
||||
from StringIO import StringIO
|
||||
|
||||
|
||||
def object_hook(dct):
|
||||
if '__class__' in dct:
|
||||
if dct['__class__'] == 'datetime':
|
||||
return datetime.datetime(dct['year'], dct['month'], dct['day'],
|
||||
dct['hour'], dct['minute'], dct['second'], dct['microsecond'])
|
||||
elif dct['__class__'] == 'date':
|
||||
return datetime.date(dct['year'], dct['month'], dct['day'])
|
||||
elif dct['__class__'] == 'time':
|
||||
return datetime.time(dct['hour'], dct['minute'], dct['second'],
|
||||
dct['microsecond'])
|
||||
elif dct['__class__'] == 'buffer':
|
||||
return buffer(base64.decodestring(dct['base64']))
|
||||
elif dct['__class__'] == 'Decimal':
|
||||
return Decimal(dct['decimal'])
|
||||
return dct
|
||||
class JSONDecoder(object):
|
||||
|
||||
decoders = {}
|
||||
|
||||
@classmethod
|
||||
def register(cls, klass, decoder):
|
||||
assert klass not in cls.decoders
|
||||
cls.decoders[klass] = decoder
|
||||
|
||||
def __call__(self, dct):
|
||||
if dct.get('__class__') in self.decoders:
|
||||
return self.decoders[dct['__class__']](dct)
|
||||
return dct
|
||||
|
||||
JSONDecoder.register('datetime',
|
||||
lambda dct: datetime.datetime(dct['year'], dct['month'], dct['day'],
|
||||
dct['hour'], dct['minute'], dct['second'], dct['microsecond']))
|
||||
JSONDecoder.register('date',
|
||||
lambda dct: datetime.date(dct['year'], dct['month'], dct['day']))
|
||||
JSONDecoder.register('time',
|
||||
lambda dct: datetime.time(dct['hour'], dct['minute'], dct['second'],
|
||||
dct['microsecond']))
|
||||
JSONDecoder.register('buffer', lambda dct: Decimal(dct['decimal']))
|
||||
|
||||
|
||||
class JSONEncoder(json.JSONEncoder):
|
||||
|
||||
serializers = {}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(JSONEncoder, self).__init__(*args, **kwargs)
|
||||
# Force to use our custom decimal with simplejson
|
||||
self.use_decimal = False
|
||||
|
||||
@classmethod
|
||||
def register(cls, klass, encoder):
|
||||
assert klass not in cls.serializers
|
||||
cls.serializers[klass] = encoder
|
||||
|
||||
def default(self, obj):
|
||||
if isinstance(obj, datetime.date):
|
||||
if isinstance(obj, datetime.datetime):
|
||||
return {'__class__': 'datetime',
|
||||
'year': obj.year,
|
||||
'month': obj.month,
|
||||
'day': obj.day,
|
||||
'hour': obj.hour,
|
||||
'minute': obj.minute,
|
||||
'second': obj.second,
|
||||
'microsecond': obj.microsecond,
|
||||
}
|
||||
return {'__class__': 'date',
|
||||
'year': obj.year,
|
||||
'month': obj.month,
|
||||
'day': obj.day,
|
||||
}
|
||||
elif isinstance(obj, datetime.time):
|
||||
return {'__class__': 'time',
|
||||
'hour': obj.hour,
|
||||
'minute': obj.minute,
|
||||
'second': obj.second,
|
||||
'microsecond': obj.microsecond,
|
||||
}
|
||||
elif isinstance(obj, buffer):
|
||||
return {'__class__': 'buffer',
|
||||
'base64': base64.encodestring(obj),
|
||||
}
|
||||
elif isinstance(obj, Decimal):
|
||||
return {'__class__': 'Decimal',
|
||||
'decimal': str(obj),
|
||||
}
|
||||
return super(JSONEncoder, self).default(obj)
|
||||
marshaller = self.serializers.get(type(obj),
|
||||
super(JSONEncoder, self).default)
|
||||
return marshaller(obj)
|
||||
|
||||
JSONEncoder.register(datetime.datetime,
|
||||
lambda o: {
|
||||
'__class__': 'datetime',
|
||||
'year': o.year,
|
||||
'month': o.month,
|
||||
'day': o.day,
|
||||
'hour': o.hour,
|
||||
'minute': o.minute,
|
||||
'second': o.second,
|
||||
'microsecond': o.microsecond,
|
||||
})
|
||||
JSONEncoder.register(datetime.date,
|
||||
lambda o: {
|
||||
'__class__': 'date',
|
||||
'year': o.year,
|
||||
'month': o.month,
|
||||
'day': o.day,
|
||||
})
|
||||
JSONEncoder.register(datetime.time,
|
||||
lambda o: {
|
||||
'__class__': 'time',
|
||||
'hour': o.hour,
|
||||
'minute': o.minute,
|
||||
'second': o.second,
|
||||
'microsecond': o.microsecond,
|
||||
})
|
||||
JSONEncoder.register(buffer,
|
||||
lambda o: {
|
||||
'__class__': 'buffer',
|
||||
'base64': base64.encodestring(o),
|
||||
})
|
||||
JSONEncoder.register(Decimal,
|
||||
lambda o: {
|
||||
'__class__': 'Decimal',
|
||||
'decimal': str(o),
|
||||
})
|
||||
|
||||
|
||||
class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
|
||||
@@ -111,7 +134,7 @@ class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
|
||||
existing method through subclassing is the prefered means
|
||||
of changing method dispatch behavior.
|
||||
"""
|
||||
rawreq = json.loads(data, object_hook=object_hook)
|
||||
rawreq = json.loads(data, object_hook=JSONDecoder())
|
||||
|
||||
req_id = rawreq.get('id', 0)
|
||||
method = rawreq['method']
|
||||
|
||||
@@ -74,6 +74,28 @@ def dump_struct(self, value, write, escape=xmlrpclib.escape):
|
||||
xmlrpclib.Marshaller.dispatch[DictType] = dump_struct
|
||||
|
||||
|
||||
class XMLRPCDecoder(object):
|
||||
|
||||
decoders = {}
|
||||
|
||||
@classmethod
|
||||
def register(cls, klass, decoder):
|
||||
assert klass not in cls.decoders
|
||||
cls.decoders[klass] = decoder
|
||||
|
||||
def __call__(self, dct):
|
||||
if dct.get('__class__') in self.decoders:
|
||||
return self.decoders[dct['__class__']](dct)
|
||||
return dct
|
||||
|
||||
XMLRPCDecoder.register('datetime',
|
||||
lambda dct: datetime.date(dct['year'], dct['month'], dct['day']))
|
||||
XMLRPCDecoder.register('time',
|
||||
lambda dct: datetime.time(dct['hour'], dct['minute'], dct['second'],
|
||||
dct['microsecond']))
|
||||
XMLRPCDecoder.register('Decimal', lambda dct: Decimal(dct['decimal']))
|
||||
|
||||
|
||||
def end_struct(self, data):
|
||||
mark = self._marks.pop()
|
||||
# map structs to Python dictionaries
|
||||
@@ -81,14 +103,7 @@ def end_struct(self, data):
|
||||
items = self._stack[mark:]
|
||||
for i in range(0, len(items), 2):
|
||||
dct[xmlrpclib._stringify(items[i])] = items[i + 1]
|
||||
if '__class__' in dct:
|
||||
if dct['__class__'] == 'date':
|
||||
dct = datetime.date(dct['year'], dct['month'], dct['day'])
|
||||
elif dct['__class__'] == 'time':
|
||||
dct = datetime.time(dct['hour'], dct['minute'], dct['second'],
|
||||
dct['microsecond'])
|
||||
elif dct['__class__'] == 'Decimal':
|
||||
dct = Decimal(dct['decimal'])
|
||||
dct = XMLRPCDecoder()(dct)
|
||||
self._stack[mark:] = [dct]
|
||||
self._value = 0
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ from trytond.pool import Pool, PoolBase
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.error import WarningErrorMixin
|
||||
from trytond.url import URLMixin
|
||||
from trytond.protocols.jsonrpc import object_hook, JSONEncoder
|
||||
from trytond.protocols.jsonrpc import JSONDecoder, JSONEncoder
|
||||
from trytond.model.fields import states_validate
|
||||
from trytond.pyson import PYSONEncoder
|
||||
from trytond.rpc import RPC
|
||||
@@ -265,7 +265,7 @@ class Wizard(WarningErrorMixin, URLMixin, PoolBase):
|
||||
self._session_id = session_id
|
||||
session = Session(session_id)
|
||||
data = json.loads(session.data.encode('utf-8'),
|
||||
object_hook=object_hook)
|
||||
object_hook=JSONDecoder())
|
||||
for state_name, state in self.states.iteritems():
|
||||
if isinstance(state, StateView):
|
||||
Target = pool.get(state.model_name)
|
||||
|
||||
Reference in New Issue
Block a user