Skip to main content

Explain exactly why and where Python objects fail JSON serialization

Project description

jsonwhy

Python's JSON encoder tells you which type failed, but not where that value is inside a large payload. jsonwhy adds the missing path and reports other problems it finds in the same pass.

from datetime import datetime

import jsonwhy

payload = {
    "users": [{"name": "Ada", "joined": datetime(2026, 7, 12)}],
    "tags": {"python", "json"},
}

jsonwhy.dumps(payload)
jsonwhy.JsonWhyError: JSON serialization failed with 2 issues:
1. $.users[0].joined: Object of type datetime is not JSON serializable.
   Fix: Convert it with value.isoformat(); preserve timezone information.
2. $.tags: Object of type set is not JSON serializable.
   Fix: Convert it to a list; sort first if deterministic output matters.

Install

python -m pip install jsonwhy

Python 3.10 or newer is required. The package has no runtime dependencies.

API

dumps() and dump() accept the standard json options. Valid input is passed through to the standard library; diagnosis only runs after serialization fails.

encoded = jsonwhy.dumps({"ok": True}, indent=2)

with open("data.json", "w", encoding="utf-8") as output:
    jsonwhy.dump({"ok": True}, output)

Use explain() when you want structured results without raising an exception:

issues = jsonwhy.explain(payload)

for issue in issues:
    print(issue.path)
    print(issue.json_pointer)
    print(issue.kind)
    print(issue.suggestion)
    print(issue.as_dict())

Each issue includes both a readable JSONPath-like path such as $.users[0].joined and an RFC 6901 json_pointer such as /users/0/joined. A pointer is None when the location contains a Python key that cannot exist in a JSON object.

Two shorter checks are also available:

jsonwhy.check(payload)                 # bool
jsonwhy.assert_serializable(payload)   # raises JsonWhyError

Value representations are useful during development but may not belong in production logs. They can be removed without calling repr() on the values:

issues = jsonwhy.explain(payload, include_value_repr=False)

jsonwhy.dumps(
    payload,
    diagnostic_include_value_repr=False,
    diagnostic_max_issues=20,
    diagnostic_max_depth=200,
)

The diagnostic options do not change encoded output. Setting include_value_repr=False controls value_repr only; paths, custom suggestion text, and exception messages may still contain application data.

jsonwhy detects unsupported values and keys, circular references, non-finite floats when allow_nan=False, failures in custom default handlers, and overly deep structures.

Custom suggestions

Applications can register advice for their own types:

class CustomerId:
    pass


jsonwhy.register_suggestion(CustomerId, "Convert it with str(value).")

This changes the suggested fix only. jsonwhy does not silently convert the value.

Command line

The CLI checks Python literals, which is useful for values such as sets, bytes, complex numbers, and tuple keys.

jsonwhy "{'payload': b'hello', 'tags': {'a', 'b'}}"
jsonwhy --json "{'payload': b'hello'}"
jsonwhy --path-style pointer "{'payload': b'hello'}"
jsonwhy --max-issues 10 --max-depth 200 "{'payload': b'hello'}"
jsonwhy --json --redact-values "{'payload': b'hello'}"

Exit status 0 means compatible, 1 means issues were found, and 2 means the input or command was invalid.

Notes

  • A failing dump() may have written part of its output before the error.
  • Diagnosis may call a custom default handler or construct a custom encoder again after the original failure. These hooks should be safe to repeat.
  • Diagnostic text can contain representations of application values. Treat it with the same care as logs and tracebacks.
  • Custom repr() implementations can perform work or have side effects before their resulting text is shortened.

Bug reports are welcome on the GitHub issue tracker.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jsonwhy-0.3.0.tar.gz (15.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jsonwhy-0.3.0-py3-none-any.whl (13.8 kB view details)

Uploaded Python 3

File details

Details for the file jsonwhy-0.3.0.tar.gz.

File metadata

  • Download URL: jsonwhy-0.3.0.tar.gz
  • Upload date:
  • Size: 15.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for jsonwhy-0.3.0.tar.gz
Algorithm Hash digest
SHA256 3043b8b19f8d4b9efe2c54607fd8edfcac64fcae87c5021ada035e68602e7bd1
MD5 316dab2984bfb5642bf606188e1063ae
BLAKE2b-256 72b8ca4e9f1e0b63d1bf166406a756efb510ec6dd045059475cc1d4a6efeca37

See more details on using hashes here.

Provenance

The following attestation bundles were made for jsonwhy-0.3.0.tar.gz:

Publisher: publish.yml on Abhay-2004/jsonwhy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jsonwhy-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: jsonwhy-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 13.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for jsonwhy-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 43688e0c4d822ba0062feb46236f6e19ba3f4f7738fc0fd80e96e2934009dd69
MD5 84bb792eeee48bb65d4b2bbbef63a7df
BLAKE2b-256 b60f2ff84834fbcc6640dcd9d257c8340123ad62e7647e7a11ccd0dc5a874e0c

See more details on using hashes here.

Provenance

The following attestation bundles were made for jsonwhy-0.3.0-py3-none-any.whl:

Publisher: publish.yml on Abhay-2004/jsonwhy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page