Allow to register different type for json/xmlrpc encoding/decoding

issue3839
review5021003
This commit is contained in:
Nicolas Évrard
2014-06-06 12:57:27 +02:00
parent 03801d399a
commit d83fc99f35
4 changed files with 98 additions and 60 deletions
+2 -2
View File
@@ -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
+71 -48
View File
@@ -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']
+23 -8
View File
@@ -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
+2 -2
View File
@@ -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)