.. currentmodule:: dataclass_wizard

Serialization Options
=====================

The following parameters can be used to fine-tune and control how the serialization of a
dataclass instance to a Python ``dict`` object or JSON string is handled.

Skip Defaults
~~~~~~~~~~~~~

A common use case is skipping fields with default values - based on the ``default``
or ``default_factory`` argument to :func:`dataclasses.field` - in the serialization
process.

The attribute ``skip_defaults`` in the inner :class:`Meta` class can be enabled, to exclude
such field values from serialization.The :meth:`to_dict` method (or the :func:`asdict` helper
function) can also be passed an ``exclude`` argument, containing a list of one or more
dataclass field names to exclude from the serialization process. An example of both these
approaches is shown below.

.. code:: python3

    from collections import defaultdict
    from dataclasses import field, dataclass

    from dataclass_wizard import JSONWizard


    @dataclass
    class MyClass(JSONWizard):

        class _(JSONWizard.Meta):
            skip_defaults = True

        my_str: str
        other_str: str = 'any value'
        optional_str: str = None
        my_list: list[str] = field(default_factory=list)
        my_dict: defaultdict[str, list[float]] = field(
            default_factory=lambda: defaultdict(list))


    print('-- Load (Deserialize)')
    c = MyClass('abc')
    print(f'Instance: {c!r}')

    print('-- Dump (Serialize)')
    string = c.to_json()
    print(string)

    assert string == '{"my_str": "abc"}'

    print('-- Dump (with `skip_defaults=False`)')
    print(c.to_dict(skip_defaults=False))

Exclude Fields
~~~~~~~~~~~~~~

You can also exclude specific dataclass fields (and their values) from the serialization
process. There are two approaches that can be used for this purpose:

* The argument ``dump=False`` can be passed in to the :func:`json_key` and :func:`json_field`
  helper functions. Note that this is a more permanent option, as opposed to the one
  below.

* The :meth:`to_dict` method (or the :func:`asdict` helper function ) can be passed
  an ``exclude`` argument, containing a list of one or more dataclass field names
  to exclude from the serialization process.

Additionally, here is an example to demonstrate usage of both these approaches:

.. code:: python3

    from typing import Annotated

    from dataclass_wizard import DataclassWizard, Alias


    class MyClass(DataclassWizard):
        my_str: str
        my_int: int
        other_str: Annotated[str, Alias('AnotherStr', skip=True)]
        my_bool: bool = Alias('TestBool', skip=True)


    data = {'my_str': 'my string',
            'my_int': 1,
            'AnotherStr': 'testing 123',
            'TestBool': True}

    print('-- From Dict')
    c = MyClass.from_dict(data)
    print(f'Instance: {c!r}')

    # dynamically exclude the `my_int` field from serialization
    additional_exclude = ('my_int',)

    print('-- To Dict')
    out_dict = c.to_dict(exclude=additional_exclude)
    print(out_dict)

    assert out_dict == {'my_str': 'my string'}

"Skip If" Functionality
~~~~~~~~~~~~~~~~~~~~~~~

The **Dataclass Wizard** offers powerful, configurable options to **skip serializing fields** under specific conditions. This functionality is available both **globally** (via the `Meta` class) and **per-field** (using type annotations or `dataclasses.Field` wrappers).

Overview
--------

You can:
- **Globally skip** fields that match a condition using ``Meta.skip_if`` or ``Meta.skip_defaults_if``.
- **Conditionally skip fields individually** using type annotations with ``SkipIf``, or the ``skip_if_field`` wrapper for ``dataclasses.Field``.

**Built-in Helpers**: For added flexibility, use helpers like ``IS_TRUTHY``, ``IS_FALSY``, and others for common conditions.
**Note**: ``SkipIfNone`` is an alias for ``SkipIf(IS(None))``.

1. Global Field Skipping
------------------------

1.1 Skip Any Field Matching a Condition
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Use the ``skip_if`` option in your dataclass's ``Meta`` configuration to skip fields that meet a specific condition during serialization.

.. code-block:: python3

    from dataclass_wizard import DataclassWizard
    from dataclass_wizard.conditions import IS_NOT


    class Example(DataclassWizard):
        class _(DataclassWizard.Meta):
            skip_if = IS_NOT(True)  # Skip if the field is not `True`.

        my_str: 'str | None'
        my_bool: bool
        other_bool: bool = False

    ex = Example(my_str=None, my_bool=True)
    assert ex.to_dict() == {'my_bool': True}  # Only `my_bool` is serialized.

1.2 Skip Fields with Default Values Matching a Condition
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Use the ``skip_defaults_if`` option to skip serializing **fields with default values** that match a condition.

.. code-block:: python3

    from dataclass_wizard import DataclassWizard
    from dataclass_wizard.conditions import IS


    class Example(DataclassWizard):
        class _(DataclassWizard.Meta):
            skip_defaults_if = IS(None)  # Skip fields with default value `None`.

        my_str: str | None
        my_bool: bool | None = False


    ex = Example(my_str=None)
    assert ex.to_dict() == {'my_str': None, 'my_bool': False}

    ex.my_bool = None
    assert ex.to_dict() == {'my_str': None}  # Explicitly set `None` values are not skipped.

1.3 Skip Fields Based on Truthy/Falsy Values
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Use the ``IS_TRUTHY`` and ``IS_FALSY`` helpers for conditions based on truthiness or falsiness.

.. code-block:: python3

    from dataclass_wizard import DataclassWizard
    from dataclass_wizard.conditions import IS_TRUTHY


    class Example(DataclassWizard):
        class _(DataclassWizard.Meta):
            skip_if = IS_TRUTHY()  # Skip fields that evaluate to True.

        my_bool: bool
        my_none: None = None


    ex = Example(my_bool=True, my_none=None)
    assert ex.to_dict() == {'my_none': None}  # Only `my_none` is serialized.

2. Per-Field Skipping
---------------------

For finer control, fields can be skipped **individually** using type annotations with ``SkipIf`` or by wrapping ``dataclasses.Field`` with ``skip_if_field``.

2.1 Using Type Annotations
^^^^^^^^^^^^^^^^^^^^^^^^^^

You can use ``SkipIf`` in conjunction with ``Annotated`` to conditionally skip individual fields during serialization.

.. code-block:: python3

    from typing import Annotated
    from dataclass_wizard import DataclassWizard
    from dataclass_wizard.conditions import SkipIf, IS


    class Example(DataclassWizard):
        my_str: Annotated['str | None', SkipIf(IS(None))]  # Skip if `my_str is None`.

2.2 Using ``skip_if_field`` Wrapper
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Use ``skip_if_field`` to add conditions directly to ``dataclasses.Field``:

.. code-block:: python3

    from dataclass_wizard import DataclassWizard, skip_if_field
    from dataclass_wizard.conditions import EQ


    class Example(DataclassWizard):
        third_str: 'str | None' = skip_if_field(EQ(''), default=None)  # Skip if empty string.

2.3 Combined Example
^^^^^^^^^^^^^^^^^^^^

Both approaches can be used together to achieve granular control:

.. code-block:: python3

    from typing import Annotated
    from dataclass_wizard import DataclassWizard, skip_if_field
    from dataclass_wizard.conditions import SkipIf, IS, EQ


    class Example(DataclassWizard):
        my_str: Annotated['str | None', SkipIf(IS(None))]  # Skip if `my_str is None`.
        third_str: 'str | None' = skip_if_field(EQ(''), default=None)  # Skip if `third_str` is ''.


    ex = Example(my_str='test', third_str='')
    assert ex.to_dict() == {'my_str': 'test'}

Key Classes and Utilities
-------------------------

- ``SkipIf``: Adds skipping logic to a field via type annotations.
- ``skip_if_field``: Wraps ``dataclasses.Field`` for inline skipping logic.
- **Condition Helpers**:

  - ``IS``, ``IS_NOT``: Skip based on identity.
  - ``EQ``, ``NE``, ``LT``, ``LE``, ``GT``, ``GE``: Skip based on comparison.
  - ``IS_TRUTHY``, ``IS_FALSY``: Skip fields based on truthiness or falsiness.
  - **Alias**: ``SkipIfNone`` is equivalent to ``SkipIf(IS(None))``.

Performance and Clarity
-----------------------

This design ensures both **performance** and **self-documenting code**, while enabling complex serialization rules effortlessly.
