0

Consider a server generating JSON messages for a to-do list.

import json

class Action(object):
    def __init__(self, what, when):
        self.what = what
        self.when = when

class Actions(object):
    def __init__(self):
        self.actions = []

    def insert(self, action):
        self.actions.append({'action': 'insert_todo',
                             'detail': {'what': action.what,
                                        'when': action.when}})

class Batch(object):
    def __init__(self):
        self.urgent_actions = Actions()
        self.ordinary_actions = Actions()
        self.urgent_actions.insert(Action('tidy up', '8am'))

def jdefault(o):
    return o.__dict__

def output_json():
    batch = Batch()
    mystr = json.dumps(batch,
                       default=jdefault,
                       indent=4)
    print(mystr)

output_json()

This works fine, and we get the message:

{
    "urgent_actions": {
        "actions": [
            {
                "action": "insert_todo",
                "detail": {
                    "what": "tidy up",
                    "when": "8am"
                }
            }
        ]
    },
    "ordinary_actions": {
        "actions": []
    }
}

But repeating actions inside both priorities of actions and in each message is asking for some clean-up.

We can do that by deriving Actions from list:

class Actions(list):
    def __init__(self, *args):
        list.__init__(self, *args)

    def insert(self, action):
        self.append({'action': 'insert_todo',
                     'detail': {'what': action.what,
                                'when': action.when}})

And we get indeed the slimmer JSON message:

{
    "urgent_actions": [
        {
            "action": "insert_todo",
            "detail": {
                "what": "8am",
                "when": "8am"
            }
        }
    ],
    "ordinary_actions": []
}

Yet, deriving from list is far from the best idea.

What other (idiomatic) way would you use to get the slimmer message without deriving from list?

The messages are to be sent through Flask, in case you'd also like to critique the use of json.dumps.

Calaf
  • 10,113
  • 15
  • 57
  • 120
  • Make a single list of actions and some of them (or all of them) have a Boolean "urgent" attribute? – Tomalak Oct 11 '19 at 10:44
  • @Tomalak We could get rid of `Batch` altogether as you suggest. The 'urgent attribute' is here a placeholder for features. That is a separate issue from `Actions`. What might be a good idea for avoiding the inheritance from `list`? – Calaf Oct 11 '19 at 10:53

1 Answers1

0

Rather than change the classes, you could delegate defining the structure of the json to the jdefault function.

def jdefault(o):
    if isinstance(o, Batch):
        return o.__dict__
    if isinstance(o, Actions):
        return o.actions
    raise TypeError("Object of type {} is not JSON serializable".format(type(o)))

Which generates the desired output:

{
    "urgent_actions": [
        {
            "action": "insert_todo",
            "detail": {
                "what": "tidy up",
                "when": "8am"
            }
        }
    ],
    "ordinary_actions": []
}

This way you separate the concerns of your objects' structure and serialisation.

snakecharmerb
  • 47,570
  • 11
  • 100
  • 153