Сериализация пользовательских классов с помощью JSON в Python 3

У меня возникают проблемы, когда JSON пытается декодировать мой сериализованный пользовательский класс с информацией о классе, закодированной в dict. Я постараюсь предоставить как можно больше информации, поэтому, пожалуйста, миритесь с длинным постом.

Я использую отличный учебник, представленный здесь в качестве ссылки. Я имею в виду раздел, в котором подробно пишется кодирование / декодирование для ваших собственных классов.

Моя структура пакета-модуля частично выглядит следующим образом:

+--- PyDev Project
    +-- src
        +-- astar
        |    +-- __init__.py
        |    +-- astar.py
        +-- common
        |    +-- __init__.py
        |    +-- service.py
        |    +-- graph.py
        |    +-- vertex.py

Каждый из этих модулей имеет свой класс (ы). Я сериализую объект класса Service в модуле common.service. Это в основном для использования через соединение Pyro.

Код сериализации:

def convert_to_builtin_type(obj):
    print('default(', repr(obj), ')')
    # Convert objects to a dictionary of their representation
    d = { '__class__':obj.__class__.__name__, 
          '__module__':obj.__module__,
        }
d.update(obj.__dict__)
return d

Код десериализации:

def dict_to_object(self, d):
    if '__class__' in d:
        class_name = d.pop('__class__')
        module_name = d.pop('__module__')
        # module = __import__(module_name)
        module = import_module(module_name)
        print('MODULE:', module)
        class_ = getattr(module, class_name)
        print('CLASS:', class_)
        args = dict( (key.encode('ascii'), value) for key, value in d.items())
        print('INSTANCE ARGS:', args)
        inst = class_(**args)
    else:
        inst = d
    return inst

Проблема возникает во время десериализации. Значения локальной переменной:

class_ = <module 'common.Service' from path-to-file.py>
class_name = 'Service'
module = <module 'common' from path-to-__init__.py>
module_name = 'common.Service'

Причина этого заключается в том, что класс_ возвращает значение package.module, а не имя класса внутри этого модуля. Технически, класс_ должен содержать common.service.Service, который является фактическим классом. Из-за этого последний оператор inst = class(** args) _ завершается с ошибкой "TypeError: модуль не вызывается".

Я знаю, что import или importlib.import_module оба импортируют этот модуль верхнего уровня, но в моем случае, как мне добраться до класса внутри модуля второго уровня? Технически, второй уровень - это модуль, а первый уровень - это пакет, поэтому мне нужен класс в pkg.module, к которому я не могу добраться.

Я надеюсь, что вопрос имел смысл и показал достаточно исследований. У меня есть больше информации, если это нужно уточнить.

РЕДАКТИРОВАТЬ: Проблема решена с предложением пользователя.

def dict_to_object(self, d):
    if '__class__' in d:
        class_name = d.pop('__class__')
        module_name = d.pop('__module__')
        # module = __import__(module_name)
        __import__(module_name)
        module = sys.modules[module_name]
        print('MODULE:', module)
        class_ = getattr(module, class_name)
        print('CLASS:', class_)
        args = dict((key, value) for key, value in d.items())
        print('INSTANCE ARGS:', args)
        inst = class_(**args)
    else:
        inst = d
    return inst

1 ответ

Решение

Идеи:

  1. Возможно, проблема в этой строке:

    import_module(module_name)
    

    пересмотреть аргументы, которые вы передаете этой функции.

  2. зная сериализатор рассола, он делает следующее вместо import_module

    import sys
    __import__(module_name)
    module = sys.modules[module_name]
    
  3. В Python 3 попробуйте также obj.__class__.__qualname__, он включает в себя имя модуля и классы вложенности.

    class X:
         class Y: pass # __qualname__ includes X.Y
    
  4. Было бы хорошо, если бы вы опубликовали рабочий код позже, потому что это может быть проблемой для многих людей.

Другие вопросы по тегам