Skip to content

Commit

Permalink
Update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
rnag committed Nov 25, 2024
1 parent 8314d09 commit 4fcaf6f
Show file tree
Hide file tree
Showing 3 changed files with 231 additions and 1 deletion.
87 changes: 87 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,93 @@ Additionally, here is an example to demonstrate usage of both these approaches:
assert out_dict == {'myStr': 'my string'}
Conditional Field Skipping
~~~~~~~~~~~~~~~~~~~~~~~~~~

Dataclass Wizard now supports **conditional skipping** of fields during serialization using global settings, per-field annotations, or field wrappers.

Quick Examples
##############

1. **Globally Skip Fields Matching a Condition**

Use the ``Meta.skip_if`` option to define a global rule for skipping fields:

.. code-block:: python3
from dataclasses import dataclass
from dataclass_wizard import JSONWizard, IS_NOT
@dataclass
class Example(JSONWizard):
class _(JSONWizard.Meta):
skip_if = IS_NOT(True) # Skip fields if not `True`.
my_bool: bool
my_str: 'str | None'
print(Example(my_bool=True, my_str=None).to_dict())
# Output: {'myBool': True}
2. **Skip Defaults Based on a Condition**
Use ``Meta.skip_defaults_if`` to skip fields with default values matching a condition.

.. note::
Using ``skip_defaults_if`` automatically enables ``skip_defaults=True``.

.. code-block:: python3
from dataclasses import dataclass
from dataclass_wizard import JSONWizard, IS
@dataclass
class Example(JSONWizard):
class _(JSONWizard.Meta):
key_transform_with_dump = 'NONE'
skip_defaults_if = IS(None) # Skip default `None` values.
my_str: 'str | None' = None
my_bool: bool = False
print(Example(my_str=None).to_dict())
# Output: {'my_bool': False}
3. **Per-Field Conditional Skipping**
Use type annotations or ``skip_if_field`` for fine-grained control:

.. code-block:: python3
from __future__ import annotations # can be removed in Python 3.10+
from dataclasses import dataclass
from typing import Annotated
from dataclass_wizard import JSONWizard, SkipIfNone, skip_if_field, EQ
@dataclass
class Example(JSONWizard):
my_str: Annotated[str | None, SkipIfNone] # Skip if `None`.
other_str: str | None = skip_if_field(EQ(''), default=None) # Skip if empty.
print(Example(my_str=None, other_str='').to_dict())
# Output: {}
Special Cases
#############

- **SkipIfNone**: Alias for ``SkipIf(IS(None))``, skips fields with a value of ``None``.
- **Condition Helpers**:

- ``IS``, ``IS_NOT``: Identity checks.
- ``EQ``, ``NE``, ``GT``, etc.: Comparison operators.
- Combine these for flexible serialization rules.

Field Properties
----------------

Expand Down
143 changes: 143 additions & 0 deletions docs/common_use_cases/serialization_options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,146 @@ Additionally, here is an example to demonstrate usage of both these approaches:
print(out_dict)
assert out_dict == {'myStr': 'my string'}
"Skip If" Functionality
~~~~~~~~~~~~~~~~~~~~~~~

The **Dataclass Wizard** now offers powerful, configurable options to **skip serializing fields** under certain 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``.

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:: python
from dataclasses import dataclass
from dataclass_wizard import JSONWizard, IS_NOT
@dataclass
class Example(JSONWizard):
class _(JSONWizard.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:: python
from __future__ import annotations # can be removed in Python 3.10+
from dataclasses import dataclass
from dataclass_wizard import JSONWizard, IS
@dataclass
class Example(JSONWizard):
class _(JSONWizard.Meta):
key_transform_with_dump = 'NONE'
skip_defaults_if = IS(None) # Skip fields with default value `None`.
my_str: str | None
other_str: str | None = None
third_str: str | None = None
my_bool: bool = False
ex = Example(my_str=None, other_str='')
assert ex.to_dict() == {
'my_str': None, # Not skipped because it was explicitly set.
'other_str': '', # Explicitly set values are always serialized.
'my_bool': False # Default values are serialized if not matching the condition.
}
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:: python
from dataclasses import dataclass
from typing import Annotated
from dataclass_wizard import JSONWizard, SkipIf, IS
@dataclass
class Example(JSONWizard)
my_str: Annotated['str | None', SkipIf(IS(True))] # Skip if `my_str is True`.
2.2 Using ``skip_if_field`` Wrapper
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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

.. code-block:: python
from dataclasses import dataclass
from dataclass_wizard import JSONWizard, skip_if_field, EQ
@dataclass
class Example(JSONWizard):
class _(JSONWizard.Meta):
pass
third_str: 'str | None' = skip_if_field(EQ(''), default=None) # Skip if field is empty string.
2.3 Combined Example
^^^^^^^^^^^^^^^^^^^^

Both approaches can be used together to achieve granular control:

.. code-block:: python
from dataclasses import dataclass
from typing import Annotated
from dataclass_wizard import JSONWizard, SkipIf, skip_if_field, IS, EQ
@dataclass
class Example(JSONWizard):
class _(JSONWizard.Meta):
pass
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.

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

This design ensures both **performance** and **self-documenting code**, while enabling complex serialization rules effortlessly.
2 changes: 1 addition & 1 deletion tests/unit/test_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -2346,7 +2346,7 @@ class _(JSONWizard.Meta):
assert ex.to_dict() == {'my_str': None}


def test_skip_if_with_condition_in_annotation_and_skip_if_field():
def test_per_field_skip_if():
"""
Test per-field `skip_if` functionality, with the ``SkipIf``
condition in type annotation, and also specified in
Expand Down

0 comments on commit 4fcaf6f

Please sign in to comment.