avivh
avivh

Reputation: 173

JSON to class instance?

I have a simple class and JSON :

@dataclass
class Point:
    x: int
    y: int

jsonString = '{"x": 3, "y": 5}'

I want to convert the JSON data to an instance of a point. from C# it's easy :

JsonConvert.DeserializeObject<Point>(jsonString);

How I can do it in Python?

Upvotes: 2

Views: 5102

Answers (4)

Richard Nienaber
Richard Nienaber

Reputation: 10564

I was doing json schema validation elsewhere so something simple like this worked for me:

point = Point(**json.loads(jsonString))

Upvotes: 4

martineau
martineau

Reputation: 123463

If the "simple class" is implemented a dataclass, as shown in your question, the JSON data could be deserialized with the generic deserialize_dataclass function shown below.

dataclasses make it fairly easy to introspect the decorated classes and the information can be used extract and convert the JSON object represented by the string.

import dataclasses
import json
dataclass = dataclasses.dataclass

def deserialize_dataclass(DataClass, json_string):
    """ Convert the JSON object represented by the string into the dataclass
        specified.
    """
    json_obj = json.loads(json_string)
    dc_data = {field.name: field.type(json_obj[field.name])
                    for field in dataclasses.fields(DataClass)}
    return DataClass(**dc_data)

@dataclass
class Point:
    x: int
    y: int


json_string = '{"x": "3", "y": "5"}'
pt = deserialize_dataclass(Point, json_string)
print(pt)  # -> Point(x=3, y=5)

Upvotes: -1

chepner
chepner

Reputation: 531155

You can use a generator expression to consume the appropriate values, by decoding the string then iterating over the values corresponding to x and y.

>>> from operator import itemgetter
>>> coords = itemgetter('x', 'y')
>>> Point(*(int(x) for x in coords(json.loads(jsonString))))
Point(x=3, y=5)

coords is a function that returns a tuple consisting of its argument's x and y values. The generator expression ensures each value is converted to an int, and the * syntax unpacks the generator into individual arguments.

A more idiomatic solution, though, would be to define a class method to construct a Point given an appropriate object:

@dataclass
class Point:
    x: int
    y: int

    @classmethod
    def from_dict(cls, d):
        return cls(d['x'], d['y'])

p = Point.from_dict(json.loads(jsonString))

You could also define a from_json class method to wrap from_dict:

@dataclass
class Point:
    x: int
    y: int

    @classmethod
    def from_dict(cls, d):
        return cls(d['x'], d['y'])

    @classmethod
    def from_json(cls, j):
        return cls.from_dict(json.loads(j))

p = Point.from_json(jsonString)

Though not shown here, the class methods provide places to do validation of the passed JSON string or argument, so you can more gracefully handle things like missing keys, extra keys, JSON values that aren't objects, etc.

Upvotes: 1

Daniel
Daniel

Reputation: 3527

How about something like this?

EDIT: you can also convert x and y to ints if they are initially strings:

# define Point class:
class Point():

    # define init function:
    def __init__(self, data):
        self.x = int(data['x'])
        self.y = int(data['y'])


# your json point:
json_data = {'x' : '2', 'y' : '3'}

# convert to Point class:
my_point = Point(json_data)

print(my_point)
print(my_point.x)
print(my_point.y)        

Upvotes: -1

Related Questions