5

With Python and JsonPickle, How do I serialize the object with a Certain Casing, eg Camel Case, Pascal, etc? The following answer below does it manually, however looking for a specific Jsonpickle solution, since it can handle complex object types .

JSON serialize a class and change property casing with Python

https://stackoverflow.com/a/8614096/15435022

class HardwareSystem:
    def __init__(self, vm_size):
        self.vm_size = vm_size
        self.some_other_thing = 42
        self.a = 'a'

def snake_to_camel(s):
    a = s.split('_')
    a[0] = a[0].lower()
    if len(a) > 1:
        a[1:] = [u.title() for u in a[1:]]
    return ''.join(a)

def serialise(obj):
    return {snake_to_camel(k): v for k, v in obj.__dict__.items()}

hp = HardwareSystem('Large')
print(json.dumps(serialise(hp), indent=4, default=serialise))
1
  • hi. Have you tried my solution ? Feel free to ask questions about it. Commented Apr 30, 2021 at 13:18

1 Answer 1

2
+50

Here's my attempt.

from importlib import import_module
import inspect
import json
import jsonpickle
import re


def snake_to_camel(s):
    a = s.split('_')
    a[0] = a[0].lower()
    if len(a) > 1:
        a[1:] = [u.title() for u in a[1:]]
    return ''.join(a)

def camel_to_snake(s):
    snake = []
    snake_len = len(s)
    for idx, char in enumerate(s):
        snake.append(char.lower())
        if idx < snake_len - 1:
            if char.islower() and s[idx+1].isupper():
                snake.append('_')
    return ''.join(snake)

def debug_output(obj):
    output = '{}({})'
    attrs = [attr + '=' + repr(getattr(obj, attr)) for attr in vars(obj)]
    return output.format(obj.__class__.__name__, ', '.join(attrs))


class SoftwareSystem:
    def __init__(self):
        self.software_rating = 'Awesome!'
    
    # Making debug output friendly
    def __repr__(self):
        return debug_output(self)


class HardwareSystem:
    def __init__(self, vm_size):
        self.vm_size = vm_size
        self.some_other_thing = 42
        self.a = 'a'
    
    # Making debug output friendly
    def __repr__(self):
        return debug_output(self)


@jsonpickle.handlers.register(HardwareSystem, base=True)
@jsonpickle.handlers.register(SoftwareSystem, base=True)
class SystemHandler(jsonpickle.handlers.BaseHandler):
    def flatten(self, obj, data):
        for k, v in obj.__dict__.items():
            data[snake_to_camel(k)] = jsonpickle.encode(v)
        return data

    def restore(self, obj):
        # Gets reference to class
        # https://stackoverflow.com/a/55559852/152016
        module_path, class_name = obj['py/object'].rsplit('.', 1)
        module = import_module(module_path)
        class_ = getattr(module, class_name)

        # Dealing with __init__ params (except first)
        params = inspect.getargs(class_.__init__.__code__)
        params = params.args[1:]

        # Preparing dict keys
        l_obj = {}
        for k, v in obj.items():
            l_obj[camel_to_snake(k)] = v

        # Instantiating constructor params
        data = {}
        for k, v in l_obj.items():
            if k in params:
                data[k] = v 
        result = class_(**data)

        # Setting other jsonpickled object attributes 
        for k, v in l_obj.items():
            if not k in params:
                setattr(result, k, v)

        return result


hw = HardwareSystem(100)
sw = SoftwareSystem()
hw.software_instance = sw
json_str = jsonpickle.encode(hw)
print(json_str)
decoded = jsonpickle.decode(json_str)
print(hw)

This has some assumptions:

  • Following your original snake_to_camel function, I've put up a camel_to_snake on decoding, that assumes only the first uppercase letter after a lowercase letter will have prepended the _ char (so awesomeABC will translate to awesome_abc, and therefore if you translate it back again it will be incorrectly awesomeAbc)
  • The above code encodes/decodes properties added after __init__ (see for example hw.software_instance above).
  • You can nest objects. I've only tried a single object nested.
  • I've added auxiliary debug_output/__repr__ functions, you may throw these away (or customize :))
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.