-
Notifications
You must be signed in to change notification settings - Fork 64
JSON example #155
Comments
The example certainly works, but it looks a wee bit artificial too me, to be perfectly honest. Yes, the So, may I propose to slightly change the example like this: def get_pet(raw_pet: object) -> Cat | Dog | None:
match raw_pet:
case {"type": "cat", "name": str(name), "breed": str(breed), "favorite_toy": str(toy)}:
return Cat(name, breed, toy)
case {"type": "dog", "name": str(name), "breed": str(breed), "leash_color": str(leash)}:
return Dog(name, breed, leash)
case {"type": "cat" | "dog"}:
raise malformed_data(repr(raw_pet))
case _:
return None # Not a known type of pet Perhaps it might also be a nice touch to add a very brief description or title saying something like "transforming data from JSON to Python data classes." |
Okay, but maybe then the example should validate everything and raise in the default case? That would actually simplify things a bit because we can get rid of the Optional ( We should also add |
Yes, that sounds good to me. I would still have the additional case then, though, and use perhaps two different exception types. One could be |
Okay, then my next version is: # Cats and Dogs
import sys
import json
from dataclasses import dataclass
@dataclass
class Animal:
pass
@dataclass
class Pet(Animal):
name: str
breed: str
@dataclass
class Dog(Pet):
leash_color: str
@dataclass
class Cat(Pet):
favorite_toy: str
def get_pets(raw: object) -> list[Cat | Dog]:
match raw:
case [*raw_pets]: # List of pets
return [get_pet(raw_pet) for raw_pet in raw_pets]
case {**raw_pet}: # Maybe a single pet
return [get_pet(raw_pet)]
case _:
raise TypeError(f"Neither a pet nor a list of pets: {raw}")
def get_pet(raw_pet: object) -> Cat | Dog:
match raw_pet:
case {"type": "cat", "name": str(name), "breed": str(breed), "favorite_toy": str(toy)}:
return Cat(name, breed, toy)
case {"type": "dog", "name": str(name), "breed": str(breed), "leash_color": str(leash)}:
return Dog(name, breed, leash)
case {"type": "cat" | "dog"}:
raise TypeError(f"Malformed pet: {raw_pet}")
case _:
raise TypeError(f"Not a pet: {raw_pet}")
def main() -> None:
raw = json.load(sys.stdin)
for pet in get_pets(raw):
print(pet)
if __name__ == "__main__":
main() However, I still find this too long for inclusion in PEP 635 (where I have a TODO suggesting a JSON example), and even if we link to it I'm less than thrilled about using case {"type": "cat", "name": name, "breed": breed, "favorite_toy": toy}: it would be more accessible as an example, but of course it fails to validate properly. |
Cleaned up as https://github.com/gvanrossum/patma/blob/master/examples/jsonpets.py I changed the structure somewhat, moving "breed" into Dog and changing it to "pattern" for Cat (tuxedo cats aren't a breed, the Internet tells me). Note that there's no way to validate the type annotations, as no type checker for Python currently supports match statements (nor the PEP 604 |
What do people think of this example? I don't think it's great (the
str(name)
etc. grates). However it does validate the JSON (ignoring malformed and extra data -- it's easy to extend it to reject those, but boring).The text was updated successfully, but these errors were encountered: