# Map a JSON Key to a Field

Note

Important: The current key transform and “custom mappings” functionality is being phased out. Please refer to the new docs for V1 Opt-in features, which introduces enhanced support for these use cases. For more details, see the Field Guide to V1 Opt‐in and the V1 Alias documentation.

This change is part of the ongoing improvements in version `v0.35.0+`, and the old functionality will no longer be maintained in future releases.

The `dataclass-wizard` library provides a set of built-in key transform helper functions that automatically transform the casing of keys in a JSON or Python `dict` object to and from dataclass field names. As mentioned in the Meta section, this key transform only applies to dataclasses at present, not to keys in `dict` objects or to sub-classes of NamedTuple or TypedDict, for example.

When converting a JSON key to a dataclass field name, the key transform function defaults to `to_snake_case()`, which converts all JSON keys to - you guessed it! - snake case, which is the leading convention in Python. Therefore, a JSON key appearing as myField, MYField, MyField, or my-field will all implicitly be mapped to a dataclass field named `my_field` by default. When converting the dataclass field back to JSON, the default key transform function is `to_camel_case()`, which transforms it back to `myField` in this case. It’s also possible to update the key transform functions used, as explained in the Meta section.

However, suppose you want to instead create a custom mapping of a JSON key to a dataclass field name. For example, a key appears in the JSON object as `myJSONKey` (case-sensitive), and you want to map it to a dataclass field that is declared as `my_str`.

The below example demonstrates how to set up a custom mapping of a JSON key name to a dataclass field. There a few different options available, so feel free to choose whichever approach is most preferable. I am myself partial to the last approach, as I find it to be the most explicit, and also one that plays well with IDEs in general.

Note

The mapping of JSON key to field below is only in addition to the default key transform as mentioned above. For example, `myNewField` is already mapped to a `my_new_field` dataclass field, and the inverse is also true.

    from dataclasses import dataclass, field
    from typing import Annotated

    from dataclass_wizard import JSONSerializable, json_field, json_key

    @dataclass
    class MyClass(JSONSerializable):

        # 1-- Define a mapping for JSON key to dataclass field in the inner
        #     `Meta` subclass.
        class Meta(JSONSerializable.Meta):
            json_key_to_field = {
                'myJSONKey': 'my_str'
            }

        # 2-- Using a sub-class of `Field`. This can be considered as an
        #     alias to the helper function `dataclasses.field`.
        my_str: str = json_field(["myField", "myJSONKey"])

        # 3-- Using `Annotated` with a `json_key` (or :class:`JSON`) argument.
        my_str: Annotated[str, json_key('myField', 'myJSONKey')]

        # 4-- Defining a value for `__remapping__` in the metadata stored
        #     within a `dataclasses.Field` class.
        my_str: str = field(metadata={
            '__remapping__': json_key('myField', 'myJSONKey')
        })

One thing to note is that the mapping to each JSON key name is case-sensitive, so passing myfield (all lowercase) will not match a myField key in a JSON or Python `dict` object.

In either case, you can confirm that the custom key mapping works as expected:

    def main():

        string = """
        {"myJSONKey": "hello world!"}
        """

        c = MyClass.from_json(string)
        print(repr(c))
        # prints:
        #   MyClass(my_str='hello world!')

        print(c)
        # prints:
        #   {
        #     "myStr": "hello world!"
        #   }

    if __name__ == '__main__':
        main()

## Map a Field Back to a JSON Key

By default, the reverse mapping (dataclass field to JSON key) will not automatically be associated by default.

You can pass the `all` parameter (or an `__all__` key, in the case of a dictionary) to also associate the inverse mapping, as shown below.

Note

If multiple JSON keys are specified for a dataclass field, only the first one provided will be used to map a field name to a JSON key.

### Using the `Meta` approach

    from typing import Union
    from dataclasses import dataclass

    from dataclass_wizard import JSONSerializable

    @dataclass
    class MyClass(JSONSerializable):

        class Meta(JSONSerializable.Meta):

            json_key_to_field = {
                # Pass `__all__` so the inverse mapping is also added.
                '__all__': True,
                # If there are multiple JSON keys for a field, the one that is
                # first defined is used in the dataclass field to JSON key mapping.
                'myJSONKey': 'my_str',
                'myField': 'my_str',
                'someBoolValue': 'my_bool',
            }

        my_str: str
        my_bool: Union[bool, str]

### Using a `dataclasses.Field()` subclass

    from typing import Union
    from dataclasses import dataclass

    from dataclass_wizard import JSONSerializable, json_field

    @dataclass
    class MyClass(JSONSerializable):
        my_str: str = json_field(
            ('myJSONKey',
             'myField'),
            # Pass `all` so the inverse mapping is also added.
            all=True
        )

        my_bool: Union[bool, str] = json_field(
            'someBoolValue', all=True
        )

### Using Annotated with a `json_key()` argument

    from dataclasses import dataclass
    from typing import Annotated, Union

    from dataclass_wizard import JSONSerializable, json_key

    @dataclass
    class MyClass(JSONSerializable):

        my_str: Annotated[str,
                          # If there are multiple JSON keys listed for a
                          # dataclass field, the one that is defined first
                          # will be used.
                          json_key('myJSONKey', 'myField', all=True)]

        my_bool: Annotated[Union[bool, str],
                           json_key('someBoolValue', all=True)]

In all the above cases, the custom key mappings apply for both the load and dump process, so now the below behavior is observed:

    def main():

        string = """
        {"myJSONKey": "hello world!", "someBoolValue": "TRUE"}
        """

        c = MyClass.from_json(string)
        print(repr(c))
        # prints:
        #   MyClass(my_str='hello world!', my_bool='TRUE')

        print(c)
        # prints:
        #   {
        #     "myJSONKey": "hello world!",
        #     "someBoolValue": "TRUE"
        #   }

    if __name__ == '__main__':
        main()