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?

3
  • stackoverflow.com/questions/1305532/… Commented Oct 16, 2021 at 19:30
  • Do you really need the class Address? By the way it should be Address(**adr)) since you are unpacking a dictionary Commented Oct 16, 2021 at 19:59
  • @cards no adr is a list not a dict the first json called j in the code is a dict. Commented Oct 17, 2021 at 14:20

3 Answers 3

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

Sign up to request clarification or add additional context in comments.

2 Comments

could you give me an exmaple of how dataclasses make it more pythonic?.
updated answer.
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

Comments

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'].

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.