0

I have a json file which contains this text:

{"_firstName": "John", "_lastName": "Baker", "_middleName": null, "_mobile": "7609984356", "_education": "Harvard", "_skills": null, "_email": "[email protected]"} 

I am trying to load it in to this Python object:

class Profile(): def __init__(self): self._firstName = None self._lastName = None self._middleName = None self._mobile = None self._education = None self._skills = [] self._email = None 

I am using:

def profileImport(self): with open("../tests/profile.json", "r") as f: self._prof.__dict__ = json.load(f) 

the line self._prof.__dict__ = json.load(f) is not properly loading the JSON data. I do not get an error from that line, but the object has all None types, indicating that the line didn't successfully load the json data. However, if I use json.loads(), I do get the valid json data as a string, which shows me that the proper file is being accessed. Why is self._prof.__dict__ = json.load(f) failing to parse the data into my object? I'm using Python 3.6.9.

4
  • why don't you do prof = Profile(**json.load(f)) Commented May 30, 2020 at 19:52
  • 3
    What is self._prof?? Please provide a minimal reproducible example Commented May 30, 2020 at 19:59
  • @juanpa.arrivillaga self._prof is an instance of the Profile() class shown exactly how it is above. Meaning, that self._prof is an instance of that class with all fields set to None. I've passed a brand new Prof() into the importer and called profileImport() on it. Commented May 31, 2020 at 7:08
  • @the_endian you're not understanding, your example isn't reproducible. Of course, you haven't provide a complete example, which is what I'm asking for - a minimal reproducible example, but when I try to assign a dict deserialized from the json you've provided to a Profile object, it works as expected. Commented May 31, 2020 at 11:49

2 Answers 2

1

I would use a a dataclass with a class method for this.

from dataclasses import dataclass, field from typing import List import json @dataclass class Profile: _firstName: str _lastName: str _mobile: str _education: str _email: str _skills: List[str] = field(default_factory=list) _middleName: str = "" def __post_init__(self): if self._skills is None: self._skills = [] if self._middleName is None: self._middleName = "" @classmethod def from_json(cls, f): return cls(**json.load(f)) with open("tmp.json") as f: profile = Profile.from_json(f) 

The idea is that the class method handles the work of calling json.load and passing the resulting key/value pairs to __init__, which is automatically generated to set the desired fields. __post_init__ takes care of converting the None values provided by the JSON object into "real" defaults.

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

2 Comments

Why do you provide types for all the fields? I do not understand the reasoning for this. I don't program a lot of Python, but mostly C and I thought that Python was mostly not staticly typed.
Python is dynamically typed. Type annotations can be used to support a level of static type checking by external tools. Here, though, they are primarily intended to signal to the dataclass decorator that the annotated-though-possibly-undefined names are to be used in constructing various methods like __init__, __eq__, etc.
0

You may not need to have an __init__ in your class to create a class instance with the values inside the json file.

Just have a @classmethod to do a load from a json file into a class instance, like so:

import json class Profile: @classmethod def from_json(cls, json_file): for key, value in json.load(open(json_file)).items(): setattr(cls, key, value) return cls 

Now you can create an instance like this:

profile = Profile.from_json('/path/to/file.json') profile._email # --> "[email protected]" 

2 Comments

This looks like a good solution. I have one question though: What if I also need to be able to populate that object's fields via another method? E.G. in one case, the user provides them via CLI and in the second case, they load them from the json file? Maybe I turn the user input directly into json and then parse from there?
@the_endian you can just create another alternative constructor. Note, the above implementation is incorrect. it sets the attribtues on the class object. This method should instantiate a profile object and initialize it from the JSON (note, your original approach works, or the alternative one in the comments: Profile(**json.load(file)) would do as well, or even the one in this answer except applied to an actual instance, and not the class

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.