0

Consider the following code

import json


class Address(object):
    def __init__(self, street, number):
        self.street = street
        self.number = number

    def __str__(self):
        return "{0} {1}".format(self.street, self.number)


class User(object):
    address: List[Address] = []

    def __init__(self, name, address: List):
        self.name = name
        for adr in address:
            self.address.append(Address(*adr)) # is this needed?

    def __str__(self):
        return "{0} ,{1}".format(self.name, self.address)


if __name__ == '__main__':
    js = '''{
    "name": "Cristian",
    "address": [{
            "street": "Sesame",
            "number": 122
        },
        {
            "street": "Sesame",
            "number": 122
        }
    ]
}'''
    j = json.loads(js)
    print(j)
    u = User(**j)
    print(u.name)
    print(u.address[0].number) 

my question is, for this type of JSON where we have a list of values. Do we need to loop through the list? Or is there a more pythonic way to simply fill this object

address: List[Address] = []

from the loaded json?

Kinder9211
  • 105
  • 1
  • 11

3 Answers3

1

You can prefer dataclasses, json dataclasses in python

from typing import List
import json

from dataclasses import dataclass, field
from dataclasses_json import dataclass_json


class Address(object):
    def __init__(self, street, number):
        self.street = street
        self.number = number

    def __str__(self):
        return "{0} {1}".format(self.street, self.number)


@dataclass_json
@dataclass
class User(object):
    address: List[Address] = field(default_factory=lambda: [])
    name: str = None

    def __str__(self):
        return "{0} ,{1}".format(self.name, self.address)


if __name__ == '__main__':
    js = '''{
    "name": "Cristian",
    "address": [{
            "street": "Sesame",
            "number": 122
        },
        {
            "street": "Sesame",
            "number": 122
        }
    ]
    }'''

    data = User.from_dict(json.loads(js))
    print(data)

If you use __str__ in User it will print

Cristian ,[{'street': 'Sesame', 'number': 122}, {'street': 'Sesame', 'number': 122}]

else if you omit __str__ in User it will print

User(address=[{'street': 'Sesame', 'number': 122}, {'street': 'Sesame', 'number': 122}], name='Cristian')

Address class can be avoided

John Byro
  • 674
  • 3
  • 13
0

I don't know if I fully understand your question. But if the problem is that you have a loop in the __init__ method of the class you could solve it with a list comprehension. Something like:

self.address = [Address(*adr) for adr in address]

We now have still the loop, but is a bit cleaner

buran
  • 13,682
  • 10
  • 36
  • 61
Sharm
  • 3
  • 3
0

I would also propose the dataclass-wizard as an alternative when working with dataclasses; which works similarly to the accepted solution, but does not use the marshmallow library to generate schemas, for example.

from __future__ import annotations

from dataclasses import dataclass, field
from typing import List

from dataclass_wizard import JSONWizard


@dataclass
class User(JSONWizard):
    address: List[Address] = field(default_factory=list)
    name: str = None

    def __str__(self):
        return "{0} ,{1}".format(self.name, self.address)


@dataclass
class Address:
    street: str
    number: int

    def __str__(self):
        return "{0} {1}".format(self.street, self.number)


if __name__ == '__main__':
    js = '''{
    "name": "Cristian",
    "address": [{
            "street": "Sesame",
            "number": 122
        },
        {
            "street": "Sesame",
            "number": 122
        }
    ]
    }'''

    data = User.from_json(js)
    print(data)

Output:

Cristian ,[Address(street='Sesame', number=122), Address(street='Sesame', number=122)]

Do note, that I've added a __future__ import, primarily to support forward-declared annotations. If you want, you can remove that and instead declare the annotation for the address field, i.e. like List['Address'].

rv.kvetch
  • 9,940
  • 3
  • 24
  • 53