Skip to content

Handling unknown keys during deserialization

If you would like to permit extra keys to to be present in a payload that is being deserialized without raising a databind.core.converter.ConversionError, you can use the databind.core.settings.ExtraKeys setting to annotate a @dataclass, an annotation or specify it globally to allow extra keys anywhere.

When using this setting, you can also record any unexpected keys so you can report them after the deserialization.

Allowing extra keys on a dataclass

# cat <<EOF | python -
from dataclasses import dataclass
from databind.core import ExtraKeys
from databind.json import load

@ExtraKeys()
@dataclass
class MyClass:
    a: int

assert load({"a": 42, "b": "ignored"}, MyClass) == MyClass(42)

Note

The ExtraKeys setting does not apply transitively to the members of the dataclass.

Allowing extra keys on a dataclass member

# cat <<EOF | python -
from dataclasses import dataclass
from databind.core import ExtraKeys
from databind.json import load
from typing_extensions import Annotated

@dataclass
class Sub:
    a: int

@dataclass
class Main:
    sub: Annotated[Sub, ExtraKeys()]

assert load({"sub": {"a": 42, "b": "ignored"}}, Main) == Main(Sub(42))

# However this:

load({"sub": {"a": 42}, "b": "not ignored!"}, Main)

# Gives:
# databind.core.converter.ConversionError: encountered extra keys: {'b'}
#  Conversion trace:
#     $: Type(__main__.Main)

Allowing extra keys everywhere

Providing the ExtraKeys() setting to the settings of a deserialization process will enable it for all schemas, except for those that have a different setting "closer by" (you can use ExtraKeys(False) to explicitly not permit extra keys).

# cat <<EOF | python -
from dataclasses import dataclass
from databind.core import ExtraKeys
from databind.json import load

@dataclass
class MyClass:
    a: int

assert load({"a": 42, "b": "ignore"}, MyClass, settings=[ExtraKeys()]) == MyClass(42)

Recording extra keys

You can also record which extra keys have been encountered to report. This is common if you want to allow but warn about unused keys in a payload.

# cat <<EOF | python -
from dataclasses import dataclass
from databind.core import format_context_trace, ExtraKeys
from databind.json import load

@dataclass
class MyClass:
    a: int

recorded = []
assert load({"a": 42, "b": "ignore"}, MyClass, settings=[ExtraKeys(recorder=lambda ctx, keys: recorded.append((ctx, keys)))]) == MyClass(42)

for ctx, keys in recorded:
    print("warning: unused keys", keys, "at")
    print(format_context_trace(ctx))

# Gives:
#
# warning: unused keys {'b'} at
#   $: Type(__main__.MyClass)