It’s easy to convert an object to JSON and JSON to an object in Python. json
module provides the following functions.
dumps
: object -> JSONloads
: JSON -> object
import json
version_info = {
"major": 2,
"minor": 1,
"patch": 0,
}
version_json = json.dumps(version_info, indent=2)
print(version_json)
# {
# "major": 2,
# "minor": 1,
# "patch": 0
# }
version_dict = json.loads(version_json)
print(version_dict)
# {'major': 2, 'minor': 1, 'patch': 0}
print(f"major: {version_dict['major']}, minor: {version_dict.get('minor')}, patch: {version_dict.get('patch')}")
json.loads
converts the specified object to a dictionary. Therefore, we can read the desired value by giving the target key name in the above way.
If it doesn’t look good for you, we can implement it in the way below instead.
import json
from types import SimpleNamespace
version_obj = json.loads(version_json, object_hook=lambda d: SimpleNamespace(**d))
print(f"major: {version_obj.major}, minor: {version_obj.minor}, patch: {version_obj.patch}")
# major: 2, minor: 1, patch: 0
In this way, the square brackets or get
function is not needed.
It’s totally fine if the converted object is used in a small scope. However, if we need to read the value many times in many places, we might make a typo for the target key name because IntelliSense doesn’t support typing here.
If the JSON is converted to a class, IntelliSense supports the typing and it’s easier to code. How can we achieve this?
Define a class that have the same name in the parameters
We have to define a class first. The class must have the same keys as the keys used in JSON data. In this example, it’s major, minor, and patch.
class Version:
def __init__(self, major: int, minor: int, patch: int) -> None:
self.major = major
self.minor = minor
self.patch = patch
Once a class is defined according to the JSON format, we can just pass the created dictionary to the class with double asterisks.
import json
version_dict = json.loads(version_json)
version = Version(**version_dict)
# IntelliSense works here
print(f"major: {version.major}, minor: {version.minor}, patch: {version.patch}")
# major: 2, minor: 1, patch: 0
IntelliSense knows the type of version
variable, so it shows possible variable names and functions.
How to convert a nested JSON to a class
Let’s consider a more complex example. Nested JSON.
import json
version_info = {
"major": 2,
"minor": 1,
"patch": 0,
}
software_info = {
"name": "my-software",
"version": version_info,
"availableVersions": ["2.1.0", "2.0.0", "1.0.0"],
}
software_json = json.dumps(software_info, indent=2)
print(software_json)
# {
# "name": "my-software",
# "version": {
# "major": 2,
# "minor": 1,
# "patch": 0
# },
# "availableVersions": [
# "2.1.0",
# "2.0.0",
# "1.0.0"
# ]
# }
This JSON has version property in it. It is the same structure as the previous example. Let’s define the corresponding class first.
# NG: This doesn't work as expected
class Software:
def __init__(
self,
name: str,
version: Dict[str, Any],
availableVersions: List[str], # can't be snake_case here
) -> None:
self.name = name
self.version = version
self.available_versions = availableVersions
The parameter name can’t be snake_case but we can make it for the property by doing this. Let’s run it in the same way as before.
import json
software_dict = json.loads(software_json)
software = Software(**software_dict)
print(f"name: {software.name}")
# name: my-software
print(f"version: {software.version}, version.major: {software.version.major}")
# Traceback (most recent call last):
# File "/workspaces/blogpost-python/src/json_load.py", line 103, in <module>
# run()
# File "/workspaces/blogpost-python/src/json_load.py", line 97, in run
# print(f"version: {software.version}, version.major: {software.version.major}")
# AttributeError: 'dict' object has no attribute 'major'
print(f"availableVersions: {software.available_versions}, availableVersions[1]: {software.available_versions[1]}")
It throws an error. It tries to access major
property but version
variable isn’t a Version
class. It’s actually a dictionary. So it must be either software.version['major']
or software.version.get('major)
.
To convert the property to a class, we must instantiate the class by using double asterisks here too.
class Software:
def __init__(
self,
name: str,
version: Dict[str, Any],
availableVersions: List[str],
) -> None:
self.name = name
self.version = Version(**version) # use double asterisks here
self.available_versions = availableVersions
Then, software.version
becomes Version
class and thus major
property can be accessed.
import json
software_dict = json.loads(software_json)
software = Software(**software_dict)
print(f"name: {software.name}")
# name: my-software
print(f"version: {software.version}, version.major: {software.version.major}")
# version: <__main__.Version object at 0x7ffa2f8e50c0>, version.major: 2
print(f"availableVersions: {software.available_versions}, availableVersions[1]: {software.available_versions[1]}")
# availableVersions: ['2.1.0', '2.0.0', '1.0.0'], availableVersions[1]: 2.0.0
As you can see this, no special treatment is needed for array type.
Comments