question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Feature Request: Optional Serde By Alias

See original GitHub issue

Hello I have a feature request that would be very beneficial to us. We are moving models in and out of a document store where attributes contribute to the size of the document. Because of this we want to shorten attributes when saving the document and rehydrate them coming out.

But there are places in our application where we don’t have access to the deserialized dataclass and instead just have a dict. We want to abstract this so we don’t have to know what the aliased fields are when working directly against a dict, and instead would like to have the ability to deserialize the dict based on either the alias or the unaliased keys.

FEATURE REQUEST: MIXED ALIAS DESERIALIZATION

Here is an example of a case where I’d like to build a new user entity from a dict. Then I’d like to deserialize it into my UserEntity. But while building my new user dict I don’t want to have to know what the aliased fields are:

@dataclass
class UserEntity(DataClassDictMixin):
  name: str = field(metadata=field_options(alias="n"))
  age: str = field(metadata=field_options(alias="a"))

new_user = { "name": "Bill", "age": 30 }
new_user_ent = UserEntity.from_dict(new_user)

This doesn’t work with DataClasDictMixin as far as I’m aware, so we created a helper mixin that translates unaliased fields to aliased fields before deserializing in the __pre_deserialization__ method:

ALLOW_UNALIASED_DESERIALIZATION = True

@classmethod
def __pre_deserialize__(cls, d: Dict[Any, Any]) -> Dict[Any, Any]:
    if cls.ALLOW_UNALIASED_DESERIALIZATION:
        alias_revmap = cls.get_alias_revmap()
        for field, alias in alias_revmap.items():
            if field in d:
                d[alias] = d[field]
                d.pop(field)
    return d

@classmethod
def get_alias_map(cls) -> Dict[str, str]:
    """
    If aliases are defined. Returns a map of the aliased fields to the non-aliased field names
    """
    if not hasattr(cls, "__alias_map__"):
        cls.__alias_map__ = {}
        for key, field in cls.__dataclass_fields__.items():
            alias = field.metadata.get("alias")
            if alias:
                cls.__alias_map__[alias] = key
    return cls.__alias_map__

@classmethod
def get_alias_revmap(cls) -> Dict[str, str]:
    """
    If aliases are defined. Returns a map of the non-aliased fields to the alias field names
    """
    if not hasattr(cls, "__alias_revmap__"):
        alias_map = cls.get_alias_map()
        cls.__alias_revamp__ = {
            v: k for k, v in alias_map.items()
        }
    return cls.__alias_revamp__

FEATURE REQUEST: OPTIONALLY DISABLE ALIAS SERIALIZATION

When serializing entities, aliases are not used by default. Once again we have a situation where we’d like to optionally opt out of alias serialization. This can be done with the TO_DICT_ADD_BY_ALIAS_FLAG, but perhaps an oversight is that you cannot make alias serialization the default and the opt out of it. If it is optional, it’s only opt in.

@dataclass
class MyEntity(DataClassDictMixin):
    a: str = field(metadata=field_options(alias="FieldA"))
    b: str = field(metadata=field_options(alias="FieldB"))

    class Config(BaseConfig):
        serialize_by_alias = True
        code_generation_options = [TO_DICT_ADD_BY_ALIAS_FLAG]


def scratch():
    me = MyEntity(
        a="Hello",
        b="World"
    )
    print(me.to_dict())
    print(me.to_dict(by_alias=False))


if __name__ == "__main__":
    scratch()

In this sample I would expect print(me.to_dict()) to use aliases during serialization, and for print(me.to_dict(by_alias=False)) to opt out of the default. But even though I set the serialize_by_alias flag it will still use the unaliased fields during serialization because I’ve also added the TO_DICT_ADD_BY_ALIAS_FLAG.

I tried removing the TO_DICT_ADD_BY_ALIAS_FLAG entirely, but when you do that you no longer have access to by_alias in the to_dict() and it will throw this exception:

to_dict() got an unexpected keyword argument 'by_alias'

I tried to get around this by overwriting the to_dict method in both my helper mixin and entity class, but that code never gets hit.

    def to_dict(self, **kwargs) -> dict:
        print("HERE")
        return super().to_dict(**kwargs)

Whether as an instance method or a class method.


Thank you for taking the time to read through my feature request.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:15 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
Fatal1tycommented, Dec 20, 2021

Looks like you might run into an issue with Arrow not being in the locals though if you are trying to resolve type from a properties name.

Ah, I see what you mean. It could be written as follows then:

class DDBDialect(Dialect):
    types = {
        Arrow: UnixArrowSerializationStrategy()
    }
0reactions
micah-williamsoncommented, Dec 30, 2021

Thanks @Fatal1ty - I’ll try this out soon (probably next week).

Read more comments on GitHub >

github_iconTop Results From Across the Web

Field attributes - Serde
#[serde(alias = "name")]. Deserialize this field from the given name or from its Rust name. May be repeated to specify multiple possible names...
Read more >
Attributes `alias` and `flatten` don't seem to work together ...
Attributes alias and flatten don't seem to work together (serde_json) #1976 ... pub struct Config { #[serde(alias = "field1")] pub new_name: Option<String>, ...
Read more >
serde: deserialize a field based on the value of another field
So if it can deserialize a Vec<u8> , then it will always result in Content::TypeA() .
Read more >
introducing the alias_all attribute in Serde - deaddabe
Parsing real-world data with Rust: introducing the alias_all ... be put in place in order to authentify to the API and send HTTP...
Read more >
serde_rustler - crates.io: Rust Package Registry
Serde Serializer and Deserializer for Rustler NIFs. ... import s, alias es and require s simplified or omitted for brevity):
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found