-
Notifications
You must be signed in to change notification settings - Fork 2
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
Add Support for letter_case #7
Comments
Exactly the same thoughts on my side. I started using this project a few days ago and what I'm missing the most is support for different casing :) |
Hi, thanks for opening this issue. I agree definitely agree that customisation could be expanded a little. So, there is a way do this already with per-field configuration option from dataclasses import dataclass, field
from fastclasses_json import dataclass_json
@dataclass_json
@dataclass
class SnakesOfCamels:
snake_one: int = field(metadata={"fastclasses_json": {"field_name": "snakeOne"}})
snake_two: int = field(metadata={"fastclasses_json": {"field_name": "snakeTwo"}})
snake_three: int = field(metadata={"fastclasses_json": {"field_name": "snakeThree"}})
SnakesOfCamels(1,2,3).to_dict() # {'snakeOne': 1, 'snakeTwo': 2, 'snakeThree': 3}
SnakesOfCamels.from_dict({'snakeOne': 1, 'snakeTwo': 2, 'snakeThree': 3}) # SnakesOfCamels(snake_one=1, snake_two=2, snake_three=3) The main problem here is that it's super repetitive, right? In case you are looking for a quick hack, that should work most cases, you can write a simple decorator Decorator Hackfrom dataclasses import dataclass
from fastclasses_json import dataclass_json
import dataclasses
def to_camel_case(field_name):
parts = field_name.split('_')
return parts[0] + ''.join(map(lambda s: s.capitalize(), parts[1:]))
def camelise(cls):
for field in dataclasses.fields(cls):
field.metadata = {
**field.metadata,
"fastclasses_json": {
**field.metadata.get("fastclasses_json", {}),
"field_name": to_camel_case(field.name)
}
}
return cls
@dataclass_json
@camelise
@dataclass
class SnakesOfCamels:
snake_one: int
snake_two: int
snake_three: int
SnakesOfCamels(1,2,3).to_dict() # {'snakeOne': 1, 'snakeTwo': 2, 'snakeThree': 3} Returning back to the main point I don't plan to start including code for various naming conventions because there will be edge cases that many may have. I think there are two different good options available which have their advantages and disadvantages. I'll put them as two subsequent comments so the Github reactions can be used on them perhaps. |
def your_field_name_rewrite(field_name):
parts = field_name.split('_')
return parts[0] + ''.join(map(lambda s: s.capitalize(), parts[1:]))
@dataclass_json(field_name_transform=your_field_name_rewrite)
@dataclass
class SnakesOfCamels:
snake_one: int
snake_two: int
snake_three: int |
@dataclass_json
@dataclass
class SnakesOfCamels:
snake_one: int = field(metadata={"fastclasses_json": {"field_name_transform": your_field_name_rewrite}})
snake_two: int = field(metadata={"fastclasses_json": {"field_name_transform": your_field_name_rewrite}})
snake_three: int = field(metadata={"fastclasses_json": {"field_name_transform": your_field_name_rewrite}}) In reality, most of the repetition can be factored out now in some way of your choosing e.g. CAML_FIELD = {"fastclasses_json": {"field_name_transform": to_camel_case}}
@dataclass_json
@dataclass
class SnakesOfCamels:
snake_one: int = field(metadata=CAMEL_FIELD)
snake_two: int = field(metadata=CAMEL_FIELD)
snake_three: int = field(metadata=CAMEL_FIELD) corrected the factored version to not include arguments given to the decorator |
Your two options both have their advantages for sure! If we're only able to go with one approach, the first approach might be a bit more intuitive as folks normally have one key format for their classes (camel, snake, or other) -- I haven't worked with a Json blob that utilizes multiple different types, and that seems to be the exception and not the rule. Being able to designate a class as a whole feels the most natural, but I do feel for folks who might want to classify on a field-by-field basis... Since the original dataclasses-json was restrictive to class level and saw no complaints, I'd be most accepting of that kind of solution to have a better mirroring of the package. |
I was thinking this through the other morning and I remembered why I have only put per-field configuration so far. With the per-class configuration, it actually gets quite tricky, because the So, here's the example: from dataclasses import dataclass, field
from fastclasses_json import dataclass_json
def to_camel_case(field_name):
parts = field_name.split('_')
return parts[0] + ''.join(map(lambda s: s.capitalize(), parts[1:]))
def to_proper_case(field_name):
return ''.join(map(lambda s: s.capitalize(), field_name.split('_')))
@dataclass_json(field_name_transform=to_camel_case)
@dataclass
class Snakes:
crowley: int
kaa: int
more_snakes: Adders
@dataclass_json(field_name_transform=to_proper_case)
@dataclass
class Mathematicians:
euclid: int
isaac_newton: int
more_mathematicians: Adders
@dataclass
class Adders:
pythagoras_of_samos: int In >>> Snakes(1,2, Adders(3)).to_dict()
{'crowley': 1, 'kaa': 2, 'moreSnakes': {'pythagorasOfSamos': 3}}
>>> Mathematicians(1, 2, Adders(3)).to_dict()
{'Euclid': 1, 'IsaacNewton': 2, 'MoreMathematicians': {'pythagorasOfSamos': 3}} or >>> Mathematicians(1, 2, Adders(3)).to_dict()
{'Euclid': 1, 'IsaacNewton': 2, 'MoreMathematicians': {'PythagorasOfSamos': 3}}
>>> Snakes(1,2, Adders(3)).to_dict()
{'crowley': 1, 'kaa': 2, 'moreSnakes': {'PythagorasOfSamos': 3}} I have some ideas, but I would need to explore them. Per-field configuration would be simpler to implement on the face of it. Having a think 🤔 |
Interesting corner case you bring up! I'm not opposed to per-field, just feels a bit redundant. You can also simply state that the top level on to_dict is the final factor that determines the case of all sub-classes, but it could be a bit harsh... I guess it really depends on how much extra effort would compiling each time add? Would it remove the "fast"ness of fastclasses-json? Or is there another alternative we can consider? |
Sorry for the quietness. @dataclass_json(field_name_transform=your_field_name_rewrite)
@dataclass
class SnakesOfCamels:
... I concede the per-field option would cause way too much needless repetition in large projects. For the problem where the options can conflict between rival dataclasses above, I solve by including the hash of the transform function in the internal to/from_dict function name when it is generated. If multiple transforms are used, they compile separately. I've published this in version 0.6.0
and there's a simple new example added to the readme: #whole-tree-configuration-options |
Upon using
dataclasses_json
, I was running into a performance issue (similar to lidatong/dataclasses-json#228) and looked for an alternative.Your project seems to have the speed I'm looking for, but is missing a crucial component: The ability to filter using camelCase.
dataclasses_json
supports camelCase via@dataclass_json(letter_case=LetterCase.CAMEL)
, but it doesn't seem that your project supports any kind of configurations. Was curious how feasible adding camelCase support would be.Thanks for all the hard work!
The text was updated successfully, but these errors were encountered: