Serializer: use alias, make it configurable for different use cases
This commit is contained in:
@@ -49,32 +49,35 @@ def sanitize_string(s: str) -> str:
|
|||||||
return json.dumps(s)[1:-1]
|
return json.dumps(s)[1:-1]
|
||||||
|
|
||||||
|
|
||||||
def dataclass_to_dict(obj: Any) -> Any:
|
def dataclass_to_dict(obj: Any, *, use_alias: bool = True) -> Any:
|
||||||
"""
|
def _to_dict(obj: Any) -> Any:
|
||||||
Utility function to convert dataclasses to dictionaries
|
"""
|
||||||
It converts all nested dataclasses, lists, tuples, and dictionaries to dictionaries
|
Utility function to convert dataclasses to dictionaries
|
||||||
|
It converts all nested dataclasses, lists, tuples, and dictionaries to dictionaries
|
||||||
|
|
||||||
It does NOT convert member functions.
|
It does NOT convert member functions.
|
||||||
"""
|
"""
|
||||||
if is_dataclass(obj):
|
if is_dataclass(obj):
|
||||||
return {
|
return {
|
||||||
# Use either the original name or name
|
# Use either the original name or name
|
||||||
sanitize_string(
|
sanitize_string(
|
||||||
field.metadata.get("original_name", field.name)
|
field.metadata.get("alias", field.name) if use_alias else field.name
|
||||||
): dataclass_to_dict(getattr(obj, field.name))
|
): _to_dict(getattr(obj, field.name))
|
||||||
for field in fields(obj)
|
for field in fields(obj)
|
||||||
if not field.name.startswith("_") # type: ignore
|
if not field.name.startswith("_") # type: ignore
|
||||||
}
|
}
|
||||||
elif isinstance(obj, list | tuple):
|
elif isinstance(obj, list | tuple):
|
||||||
return [dataclass_to_dict(item) for item in obj]
|
return [_to_dict(item) for item in obj]
|
||||||
elif isinstance(obj, dict):
|
elif isinstance(obj, dict):
|
||||||
return {sanitize_string(k): dataclass_to_dict(v) for k, v in obj.items()}
|
return {sanitize_string(k): _to_dict(v) for k, v in obj.items()}
|
||||||
elif isinstance(obj, Path):
|
elif isinstance(obj, Path):
|
||||||
return sanitize_string(str(obj))
|
return sanitize_string(str(obj))
|
||||||
elif isinstance(obj, str):
|
elif isinstance(obj, str):
|
||||||
return sanitize_string(obj)
|
return sanitize_string(obj)
|
||||||
else:
|
else:
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
return _to_dict(obj)
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar("T", bound=dataclass) # type: ignore
|
T = TypeVar("T", bound=dataclass) # type: ignore
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ from pathlib import Path
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
# Functions to test
|
# Functions to test
|
||||||
from clan_cli.api import (
|
from clan_cli.api import dataclass_to_dict, from_dict
|
||||||
dataclass_to_dict,
|
|
||||||
from_dict,
|
|
||||||
)
|
|
||||||
from clan_cli.errors import ClanError
|
from clan_cli.errors import ClanError
|
||||||
from clan_cli.inventory import (
|
from clan_cli.inventory import (
|
||||||
Inventory,
|
Inventory,
|
||||||
@@ -87,6 +84,7 @@ def test_simple_field_missing() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_deserialize_extensive_inventory() -> None:
|
def test_deserialize_extensive_inventory() -> None:
|
||||||
|
# TODO: Make this an abstract test, so it doesn't break the test if the inventory changes
|
||||||
data = {
|
data = {
|
||||||
"meta": {"name": "superclan", "description": "nice clan"},
|
"meta": {"name": "superclan", "description": "nice clan"},
|
||||||
"services": {
|
"services": {
|
||||||
@@ -130,7 +128,16 @@ def test_alias_field() -> None:
|
|||||||
data = {"--user-name--": "John"}
|
data = {"--user-name--": "John"}
|
||||||
expected = Person(name="John")
|
expected = Person(name="John")
|
||||||
|
|
||||||
assert from_dict(Person, data) == expected
|
person = from_dict(Person, data)
|
||||||
|
|
||||||
|
# Deserialize
|
||||||
|
assert person == expected
|
||||||
|
|
||||||
|
# Serialize with alias
|
||||||
|
assert dataclass_to_dict(person) == data
|
||||||
|
|
||||||
|
# Serialize without alias
|
||||||
|
assert dataclass_to_dict(person, use_alias=False) == {"name": "John"}
|
||||||
|
|
||||||
|
|
||||||
def test_path_field() -> None:
|
def test_path_field() -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user