Convert JSON to an object with only required fields

Viewed 187

Is there a way to convert json in to an object with only required fields, such that extra fields are ignored, and if the required fields do not exits throw an error?

If an object's field matches exactly with json fields, we could use something like this:

import json
class Person:
   def __init__(self, name, age):
       self.name = name
       self.age = age

test_json = '{"name": "user", "age":"50"}'
test_dict = json.loads(test_json)
test_obj = Person(**test_dict)

However, I would want the code to silently ignore extra fields in json for example:

test_json = '{"name": "user", "age":"50", "hobby":"swimming"}'

And if required fields are missing, throw an error

test_json = '{"name": "user", "hobby":"swimming"}'

I know you can add checks in when initializing the obj from the dictionary. But there are many fields and the json can come from different places thus formatting could change, so I wonder if there is a library could help achieve the above?

3 Answers

In order to get the extra fields in the object, you can use keyworded arguments (kwargs). For instance, this code can take any number of arguments (larger than 2 since the name and age must be there).

class Person:
    def __init__(self, name, age, **kwargs):
        self.name = name
        self.age = age
        print(kwargs)

You can tinker around with this and see if you can get it to fully work as desired.

this code allows you to get only dictionary keys that match the names of your class attributes using the inspect module:

import inspect
import json

class Person:
   def __init__(self, name, age):
       self.name = name
       self.age = age

getting the required field in your class initialization, this would recognize that you will need a name and age variables in your class __init__ method:

argspec = inspect.getfullargspec(Person.__init__)
required = argspec.args
if argspec.defaults:
    required = required[:-len(argspec.defaults)]
required.remove('self')

keeping only the names that match object attributes:

test_json = '{"name": "user", "age":"50", "foo": "bar", "bar": "baz"}'
test_dict = json.loads(test_json)
test_dict = {k:v for k, v in test_dict.items() if k in required}

initializing the object:

test_obj = Person(**test_dict)

You can you Pydantic and define your class like in the example bellow:

import json
from pydantic import BaseModel

class Person(BaseModel):
    name: str
    age: int

# Ignore the extra field
test_json_extra_field = '{"name": "user", "age":"50", "hobby":"swimming"}'
person_01 = Person(**json.loads(test_json_extra_field))
print(person_01)

# throw error because age is not in the json
test_json_no_required_field = '{"name": "user", "hobby":"swimming"}' 
person_02 = Person(**json.loads(test_json_no_required_field))
print(person_02)

Pydantic BaseModel will ignore the extra field in test_json_extra_field and throw an error in test_json_no_required_field because age is not in the json info.

Related