dimagi/jsonobject

Question: Sequence of Objects

Closed this issue · 2 comments

Hi,

I'm trying to handle the case where I want to transform a JSON dictionary to a sequence of objects (e.g. List[T]). For instance, I create a model for an Error such as:

class Error(JsonObject):
    message     = StringProperty()
    status_code = IntegerProperty()
    error_code  = IntegerProperty()

I can call Error(json['data']) when my JSON looks like this:

{
  "data": {
    "message": "some error message",
    "status_code": 404,
    "error_code":  -1234
  }
}

But sometimes my JSON response looks like this instead:

{
  "data": [
     { 
        "message": "err message 1",
        "status_code": 500,
        "error_code": -5677
     },
     {
       "message": "err message 2",
       "status_code": 400,
       "error_code": -9876
     }
   ]
}

In this case what I want is to be able to call Error(json['data']) and get back a List[Error]. Or at least, I want to be able to define an object Errors that I can call like Errors(json['data']) and get my List[Error]. I know I can define a class such as:

class Errors(JsonObject):
    errors     = ListProperty(Error)

but that does not preserve the semantics of a List[Error]. I tried to fudge with __ methods such as __getitem__, __setattr__, etc to make Errors look like a container / List, but I get into trouble since some of those are already defined in JsonObject. So far, what I am doing is this:

class Errors(object):
    def __new__(cls, json: Dict[str, Union[int, str, bool]]) -> List[Error]:
        return [Error(elem) for elem in json]

Is there a better way to go about this? Does the library provide any way of doing this that I'm missing?

Thanks!

Hi @adrianbn, I think the short answer is that there's nothing specifically in this library that lets you define a custom list type (that isn't a property of a object type).

The longer answer is (getting outside the scope of the library for a moment) that my two cents is that it sounds like what you want is maybe just a function that takes in a list of dicts and returns the list of those dicts wrapped with Error:

def wrap_errors(elems):
    return [Error(elem) for elem in elems]

or something like that. Alternatively, you could define your schema one level up to include the "data" property:

class ErrorListData(JsonObject):
    data = ListProperty(Error)  # with the Error you defined above

Then, you can date the original json

json = {
  "data": [
     { 
        "message": "err message 1",
        "status_code": 500,
        "error_code": -5677
     },
     {
       "message": "err message 2",
       "status_code": 400,
       "error_code": -9876
     }
   ]
}

and wrap it with ErrorListData(json).

Let me know if I misunderstood your point. Otherwise I can go ahead and close the issue.

Thanks @dannyroberts, those are good suggestions. I'm closing this one :)