-
-
Notifications
You must be signed in to change notification settings - Fork 31
V1: Key Case Transformation
This section provides details about key casing and its transformation in the upcoming v1 major release. Additionally, it highlights the v1 opt-in functionality available in v0.33.0.
When loading JSON, the default behavior is a 1:1 mapping between JSON keys and dataclass fields. For instance:
- A field named
my_strin a dataclass maps directly to a JSON keymy_str.
KeyCase introduces several key casing transformation options. Shorthand notations (single letters) are available for convenience.
-
C|CAMEL: Converts strings to camel case.
Example:my_field_name→myFieldName -
P|PASCAL: Converts strings to Pascal case (upper camel case).
Example:my_field_name→MyFieldName -
K|KEBAB: Converts strings to kebab/lisp case.
Example:myFieldName→my-field-name -
S|SNAKE: Converts strings to snake case.
Example:myFieldName→my_field_name -
A|AUTO: Automatically maps JSON keys to dataclass fields by trying all valid transformations at runtime. Results are cached for subsequent lookups.
Example:My-Field-Name→my_field_name
You can override the default behavior using one of the following methods:
@dataclass
class Test(JSONWizard, key_case='C'):
my_str: str
my_bool_test: bool
my_float: float = 1.23
d = {'myStr': 'test', 'myBoolTest': True}
print(repr(Test.from_dict(d)))
# Output: Test(my_str='test', my_bool_test=True, my_float=1.23)@dataclass
class Test(JSONWizard):
class _(JSONWizard.Meta):
v1 = True
v1_key_case = 'CAMEL'
my_str: str
my_bool_test: bool
my_float: float = 1.23
d = {'myStr': 'test', 'myBoolTest': True, 'MyFloat': 42}
print(repr(Test.from_dict(d)))
# Output: Test(my_str='test', my_bool_test=True, my_float=1.23)The AUTO key casing mode (A) enables automatic detection of valid JSON key casing formats. It sequentially tries:
- Exact match with the dataclass field.
-
camelCase. -
PascalCase. -
kebab-case. -
Upper-Kebab. -
Upper_Snake. -
snake_case.
The transformation result is cached for optimized future lookups.
@dataclass
class Test(JSONWizard, key_case='AUTO'):
my_str: str
my_bool_test: bool
my_int: int
my_float: float = 1.23
d = {'My-Str': 'test', 'myBoolTest': True, 'MyInt': 123, 'my_float': 42}
print(repr(Test.from_dict(d)))
# Output: Test(my_str='test', my_bool_test=True, my_int=123, my_float=42.0)Note
UPDATE: This issue is resolved in #175. This section is outdated and will be updated shortly to reflect that.
There is one known limitation with the AUTO key casing transform and aliases in general. In v1, the library iterates over the dataclass fields instead of the input JSON object o. This design can lead to issues when multiple JSON keys map to a single dataclass field, as illustrated in the following example:
from dataclasses import dataclass
from dataclass_wizard import LoadMeta, JSONWizard, fromdict
@dataclass
class Container:
id: int
my_elements: list['MyElement']
@dataclass
class MyElement:
order_index: int
status_code: 'int | str'
d = {
'id': '123',
'myElements': [
{'orderIndex': 111, 'statusCode': '200'},
{'order_index': '222', 'status_code': 404}
]
}
LoadMeta(v1=True, v1_key_case='AUTO').bind_to(Container)
# Failure!
c = fromdict(Container, d)Running the above code results in the following error:
dataclass_wizard.errors.MissingFields: `MyElement.__init__()` is missing required fields.
- Provided: []
- Missing: ['order_index', 'status_code']
- Expected Keys: ['orderIndex', 'statusCode']
- Input JSON: {"order_index": "222", "status_code": 404}
- Key Transform: AUTO
Resolution: Ensure that all required fields are provided in the input. For more details, see:
https://github.com/rnag/dataclass-wizard/discussions/167
This behavior is expected because, with the current v1 implementation, there is no mechanism to map multiple JSON keys to a single dataclass field. As a result, the first key encountered becomes the "alias" for that field.
A test case replicating the example above will be created and marked as a TODO item for future resolution. This serves as a reminder to address the limitation in a future release.