'Create a class from a nested JSON

I am struggling with this problem, I have to make a class out of a json-rpc client I get responses like this

{"jsonrpc":"2.0","id":1,"result":{"os":"linux","platform":"ubuntu","platformFamily":"debian","platformVersion":"20.04","kernelVersion":"5.4.0-107-generic","memoryTotal":4021989376,"memoryFree":725327872,"numCPU":2,"numGoroutine":169645}}

I'd like to extrapolate all the keywords/values under result but i don't have any idea how to do...

I tried with this:

class foo:
    def __init__(self, jsonrpc, id, result):
        self.jsonrpc = jsonrpc
        self.id = id
        self.result = result              

    @classmethod
    def from_json(cls, json_str):
        json_dict = json.loads(json_str)
        return cls(**json_dict)

But I have the class on the upper level, and is not useful for me. I tried also to add the other keywords in the constructor but seems cannot reach the level under so compiler say arguments are missing.



Solution 1:[1]

I would personally suggest using the dataclass-wizard for this task. This is a third-party JSON de/serialization library built on top of the Python dataclasses module.

Additionally, the library exposes a command-line utility named wiz, which can be used to generate a dataclass schema for arbitrary JSON input, which would certainly be useful in this case.

For example - on Mac/Linux:

$ echo '{"jsonrpc":"2.0","id":1,"result":{"os":"linux","platform":"ubuntu","platformFamily":"debian","platformVersion":"20.04","kernelVersion":"5.4.0-107-generic","memoryTotal":4021989376,"memoryFree":725327872,"numCPU":2,"numGoroutine":169645}}' \
  | wiz gs -x

Results in the following dataclass schema:

from __future__ import annotations

from dataclasses import dataclass

from dataclass_wizard import JSONWizard


@dataclass
class Data(JSONWizard):
    """
    Data dataclass

    """
    jsonrpc: float | str
    id: int
    result: Result


@dataclass
class Result:
    """
    Result dataclass

    """
    os: str
    platform: str
    platform_family: str
    platform_version: float | str
    kernel_version: str
    memory_total: int
    memory_free: int
    num_cpu: int
    num_goroutine: int

And you can then load the JSON string in the original question above as a dataclass instance, using the from_json method:

string = '{"jsonrpc":"2.0", ...}'

instance = Data.from_json(string)
print(repr(instance))

NB: One minor point that you might notice, is that for some of the JSON values like "jsonrpc": "2.0", the generated dataclass schema shows as Union[float, str].

You can then force-resolve these encoded numeric strings to their corresponding numeric Python types, by passing the -f / --force-strings option to the wiz tool. The generated dataclass field should then show asjsonrpc: float for example.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1