Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support validation aliases #828

Open
Ravencentric opened this issue Mar 25, 2025 · 2 comments
Open

Support validation aliases #828

Ravencentric opened this issue Mar 25, 2025 · 2 comments

Comments

@Ravencentric
Copy link

Ravencentric commented Mar 25, 2025

Description

Currently rename on a Struct is always two-ways.

import msgspec
import re

# Data from a third-party API
data = """\
{
    "userId": 456,
    "firstName": "Jane",
    "lastName": "Smith",
    "emailAddress": "jane.smith@example.com"
}
"""

def snake_to_camel(name: str) -> str:
    return re.sub(r'_([a-z])', lambda m: m.group(1).upper(), name)

class User(msgspec.Struct, rename=snake_to_camel):
    user_id: int
    first_name: str
    last_name: str
    email_address: str

user = msgspec.json.decode(data, type=User)

print(msgspec.json.encode(user))
# Current: b'{"userId":456,"firstName":"Jane","lastName":"Smith","emailAddress":"jane.smith@example.com"}'
# Wanted: b'{"user_id":456,"first_name":"Jane","last_name":"Smith","email_address":"jane.smith@example.com"}'

The feature request is to add support for something like validation_alias=callable that behaves similarly to rename but only one way (the decode step).

For some prior art, pydantic has 3 ways to define aliases:

The alias parameter is used for both validation and serialization. If you want to use different aliases for validation and serialization respectively, you can use thevalidation_alias and serialization_alias parameters, which will apply only in their respective use cases.

@Ravencentric
Copy link
Author

My use case is that I want to provide two public methods: to_json and from_json where the following holds true:

class User(msgspec.Struct):
    def to_json() -> str: ...
    @classmethod
    def from_json(cls, data: str) -> str: ...

user = User(...)
assert User.from_json(user.to_json()) == user # True

But the users shouldn't have to see or care about an implementation detail (i.e, the casing used by the API).

@Ravencentric
Copy link
Author

Something I realised while thinking about this is that it can possibly be added as rename=... parameter to the msgspec.*.Encoder and msgspec.*.Decoder classes for a significantly more fine grained control over aliases across the conversion boundaries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant