How to change json encoding behaviour for serializable python object?

前端 未结 13 1218
无人及你
无人及你 2020-12-02 09:47

It is easy to change the format of an object which is not JSON serializable eg datetime.datetime.

My requirement, for debugging purposes, is to alter the way some cu

13条回答
  •  情深已故
    2020-12-02 10:13

    I try to change the default resolver priority and change the default iterator outputs to achieve your purposes.

    1. change the default resolver priority, executed precede all standard type verifying:

      Inherits the json.JSONEncoder and overrides the iterencode() method.

      All values should be wrapped by ValueWrapper type, avoid the values are resolved by default standard resolvers.

    2. change the default iterator output;

      Implement three custom wrapper classes ValueWrapper, ListWrapper, and DictWrapper. The ListWrapper implement __iter__() and the DictWrapper implement __iter__(), items() and iteritems().

    import datetime
    import json
    
    class DebugJsonEncoder(json.JSONEncoder):
        def iterencode(self, o, _one_shot=False):
            default_resolver = self.default
            # Rewrites the default resolve, self.default(), with the custom resolver.
            # It will process the Wrapper classes
            def _resolve(o):
                if isinstance(o, ValueWrapper):
                    # Calls custom resolver precede others. Due to the _make_iterencode()
                    # call the custom resolver following by all standard type verifying 
                    # failed. But we want custom resolver can be executed by all standard 
                    # verifying.
                    # see https://github.com/python/cpython/blob/2.7/Lib/json/encoder.py#L442
                    result = default_resolver(o.data)
                    if (o.data is not None) and (result is not None):
                        return result
                    elif isinstance(o.data, (list, tuple)):
                        return ListWrapper(o.data)
                    elif isinstance(o.data, dict):
                        return DictWrapper(o.data)
                    else:
                        return o.data
                else:
                    return default_resolver(o)
    
            # re-assign the default resolver self.default with custom resolver.
            # see https://github.com/python/cpython/blob/2.7/Lib/json/encoder.py#L161
            self.default = _resolve
            # The input value must be wrapped by ValueWrapper, avoid the values are 
            # resolved by the standard resolvers.
            # The last one arguemnt _one_shot must be False, we want to encode with
            # _make_iterencode().
            # see https://github.com/python/cpython/blob/2.7/Lib/json/encoder.py#L259
            return json.JSONEncoder.iterencode(self, _resolve(ValueWrapper(o)), False)
    
    
    class ValueWrapper():
        """
        a wrapper wrapped the given object
        """
    
        def __init__(self, o):
            self.data = o
    
    class ListWrapper(ValueWrapper, list):
        """
        a wrapper wrapped the given list
        """
    
        def __init__(self, o):
            ValueWrapper.__init__(self, o)
    
        # see https://github.com/python/cpython/blob/2.7/Lib/json/encoder.py#L307
        def __iter__(self):
            for chunk in self.data:
                yield ValueWrapper(chunk)
    
    class DictWrapper(ValueWrapper, dict):
        """
        a wrapper wrapped the given dict
        """
    
        def __init__(self, d):
            dict.__init__(self, d)
    
        def __iter__(self):
            for key, value in dict.items(self):
                yield key, ValueWrapper(value)
    
        # see https://github.com/python/cpython/blob/2.7/Lib/json/encoder.py#L361
        def items(self):
            for key, value in dict.items(self):
                yield key, ValueWrapper(value)
    
        # see https://github.com/python/cpython/blob/2.7/Lib/json/encoder.py#L363
        def iteritems(self):
            for key, value in dict.iteritems(self):
                yield key, ValueWrapper(value)
    
    
    def json_debug_handler(obj):
        print("object received:")
        print type(obj)
        print("\n\n")
        if  isinstance(obj, datetime.datetime):
            return obj.isoformat()
        elif isinstance(obj,mDict):
            return {'orig':obj , 'attrs': vars(obj)}
        elif isinstance(obj,mList):
            return {'orig':obj, 'attrs': vars(obj)}
        else:
            return None
    
    
    class mDict(dict):
        pass
    
    class mList(list):
        pass
    
    
    def test_debug_json():
        games = mList(['mario','contra','tetris'])
        games.src = 'console'
        scores = mDict({'dp':10,'pk':45})
        scores.processed = "unprocessed"
        test_json = { 'games' : games , 'scores' : scores , 'date': datetime.datetime.now(), 'default': None}
        print(json.dumps(test_json,cls=DebugJsonEncoder,default=json_debug_handler))
    
    if __name__ == '__main__':
        test_debug_json()
    

提交回复
热议问题