John Difool
John Difool

Reputation: 5702

How to deal with a single value when implementing jsonpickle custom handlers?

I wrote a custom handler for jsonpickle in order to transform an enum value before serializing the object container.

import jsonpickle
from enum import Enum


class Bar(Enum):
    A = 1
    B = 2


class Foo:
    def __init__(self):
        self.hello = 'hello'
        self.bar = [Bar.A, Bar.B]


class Handler(jsonpickle.handlers.BaseHandler):

    def flatten(self, obj, data):  # data contains {}
        print(obj)

        ### How should I handle the enum? ###

        return data


jsonpickle.handlers.registry.register(Bar, Handler)


def main():
    fizbuz = Foo()
    encoded = jsonpickle.encode(fizbuz)
    print(encoded)


if __name__ == '__main__':
    main()

The handler is called with the obj containing the enum value all right. But then the data dict contains already a key, value pair so I can't just return a single value representing the enum.

So my question is what should be the key, value pair I need to add to the data dict when I am custom-handling elements that return one unique value while I need to fit it in the data dict that has been pre=-populated with reflection data that will be needed for the object to be reconstructed later.

Upvotes: 3

Views: 1841

Answers (1)

Software Prophets
Software Prophets

Reputation: 2976

I don't understand why you're concerned about the dict that is supplied? That is an input for your use to aid in your handler code and doesn't need to be utilized at all if you don't want it. It isn't {}, as you indicated, if you use encode without unpicklable=False. If you don't include that, then you actually get this:

{'py/object': '__main__.Bar'}

All that is providing is the type of the variable and you wrote the handler, so you naturally know the type.

If you want a nice JSON output of your enum, just tell Python that obj of the flatten function is a Bar type (optional, but nice for the IDE and for later maintenance), like this:

def flatten(self, obj: Bar, data):

Then, just return obj.name. Doing this, the output is:

{"py/object": "__main__.Foo", "bar": ["A", "B"], "hello": "hello"}

Of course, we can make it cleaner by telling the encoder not to worry about decoding later with:

unpicklable=False

The final output is then:

{"bar": ["A", "B"], "hello": "hello"}

The whole example code:

import jsonpickle
from enum import Enum


class Bar(Enum):
    A = 1
    B = 2


class Foo:
    def __init__(self):
        self.hello = 'hello'
        self.bar = [Bar.A, Bar.B]


class Handler(jsonpickle.handlers.BaseHandler):

    def flatten(self, obj: Bar, data):
        print(obj)

        return obj.name


jsonpickle.handlers.registry.register(Bar, Handler)


def main():
    fizbuz = Foo()
    encoded = jsonpickle.encode(fizbuz, unpicklable=False)
    print(encoded)


if __name__ == '__main__':
    main()

Generic Approach

You can also generically handle all enums by using a class like this:

from enum import Enum

import jsonpickle


class JsonEnumHandler(jsonpickle.handlers.BaseHandler):

    def restore(self, obj):
        pass

    def flatten(self, obj: Enum, data):
        return obj.name

Then, register every enum in your JSON like this:

    jsonpickle.handlers.registry.register(LimitType, JsonEnumHandler)
    jsonpickle.handlers.registry.register(DeviceType, JsonEnumHandler)

Upvotes: 0

Related Questions