Serializer: use alias, make it configurable for different use cases

This commit is contained in:
Johannes Kirschbauer
2024-07-29 09:00:24 +02:00
parent d53ac276a7
commit 395a7fc70e
2 changed files with 40 additions and 30 deletions

View File

@@ -49,7 +49,8 @@ def sanitize_string(s: str) -> str:
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
@@ -60,15 +61,15 @@ def dataclass_to_dict(obj: Any) -> Any:
return {
# Use either the original name or name
sanitize_string(
field.metadata.get("original_name", field.name)
): dataclass_to_dict(getattr(obj, field.name))
field.metadata.get("alias", field.name) if use_alias else field.name
): _to_dict(getattr(obj, field.name))
for field in fields(obj)
if not field.name.startswith("_") # type: ignore
}
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):
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):
return sanitize_string(str(obj))
elif isinstance(obj, str):
@@ -76,6 +77,8 @@ def dataclass_to_dict(obj: Any) -> Any:
else:
return obj
return _to_dict(obj)
T = TypeVar("T", bound=dataclass) # type: ignore

View File

@@ -4,10 +4,7 @@ from pathlib import Path
import pytest
# Functions to test
from clan_cli.api import (
dataclass_to_dict,
from_dict,
)
from clan_cli.api import dataclass_to_dict, from_dict
from clan_cli.errors import ClanError
from clan_cli.inventory import (
Inventory,
@@ -87,6 +84,7 @@ def test_simple_field_missing() -> 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 = {
"meta": {"name": "superclan", "description": "nice clan"},
"services": {
@@ -130,7 +128,16 @@ def test_alias_field() -> None:
data = {"--user-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: