jcrist / msgspec

A fast serialization and validation library, with builtin support for JSON, MessagePack, YAML, and TOML
https://jcristharif.com/msgspec/
BSD 3-Clause "New" or "Revised" License
2.3k stars 67 forks source link

Converting all Structs (including nested ones) to dict. #594

Closed zulqasar closed 10 months ago

zulqasar commented 10 months ago

Question

structs.asdict does not convert nested Struct to dict. Am I missing out something?

zulqasar commented 10 months ago

Currently I am using this fix.

import msgspec

class Dog(msgspec.Struct):
    name: str
    age: int

class Owner(msgspec.Struct):
    name: str
    dogs: list[Dog]

owner = Owner('A dog owner', [Dog('Doggie', 2)])

msgspec.structs.asdict(owner)
# >> {'name': 'A dog owner', 'dogs': [Dog(name='Doggie', age=2)]}

# Fix
encoder = msgspec.json.Encoder()
decoder = msgspec.json.Decoder()

decoder.decode(encoder.encode(owner))
#>> {'name': 'A dog owner', 'dogs': [{'name': 'Doggie', 'age': 2}]}
jcrist commented 10 months ago

Am I missing out something?

msgspec.structs.asdict doesn't recurse into struct fields when converting structs to dicts, it only applies to the top-level struct.

To recurse through all fields, lowering complex types like structs/dataclasses/attrs/... to simpler types like int/str/dict/... you'll want to use msgspec.to_builtins. This is effectively the same as msgspec.json.decode(msgspec.json.encode(obj)), but is much more efficient.

See docs here and here.

In [2]: import msgspec
   ...: 
   ...: class Dog(msgspec.Struct):
   ...:     name: str
   ...:     age: int
   ...: 
   ...: class Owner(msgspec.Struct):
   ...:     name: str
   ...:     dogs: list[Dog]
   ...: 
   ...: owner = Owner('A dog owner', [Dog('Doggie', 2)])

In [3]: msgspec.to_builtins(owner)
Out[3]: {'name': 'A dog owner', 'dogs': [{'name': 'Doggie', 'age': 2}]}