{"id":7065,"library":"canonicaljson","title":"Canonical JSON","description":"Canonical JSON is a Python library designed to produce a deterministic, byte-for-byte consistent JSON serialization of Python data structures. This is crucial for applications requiring cryptographic hashing or signatures where the exact byte representation of JSON data must be consistent across different environments and executions. The library is currently at version 2.0.0 and maintains an active development and release cadence.","status":"active","version":"2.0.0","language":"en","source_language":"en","source_url":"https://github.com/matrix-org/python-canonicaljson","tags":["json","canonicalization","serialization","data integrity","cryptography","hashing"],"install":[{"cmd":"pip install canonicaljson","lang":"bash","label":"Install stable version"}],"dependencies":[],"imports":[{"note":"The primary function `encode_canonical_json` is typically imported directly from the top-level package.","wrong":"import canonicaljson.encode_canonical_json","symbol":"encode_canonical_json","correct":"from canonicaljson import encode_canonical_json"},{"note":"Needed for custom type serialization in v2.0.0 and later.","symbol":"register_preserialisation_callback","correct":"from canonicaljson import register_preserialisation_callback"}],"quickstart":{"code":"import canonicaljson\nfrom typing import Any, Dict\n\ndata = {\"b\": 2, \"a\": 1, \"nested\": {\"y\": 2, \"x\": 1}, \"list\": [3, 1, 2]}\ncanonical_bytes = canonicaljson.encode_canonical_json(data)\nprint(f\"Canonical JSON: {canonical_bytes.decode('utf-8')}\")\n# Expected: {\"a\":1,\"b\":2,\"list\":[3,1,2],\"nested\":{\"x\":1,\"y\":2}}\n\n# Example for custom types (v2.0.0+)\nclass CustomObject:\n    def __init__(self, value):\n        self.value = value\n\ndef custom_serializer(obj: CustomObject) -> Dict[str, Any]:\n    return {\"custom_value\": obj.value, \"_type\": \"CustomObject\"}\n\ncanonicaljson.register_preserialisation_callback(CustomObject, custom_serializer)\n\ncustom_data = {\"item\": CustomObject(\"hello\")}\ncustom_canonical_bytes = canonicaljson.encode_canonical_json(custom_data)\nprint(f\"Canonical JSON with Custom Object: {custom_canonical_bytes.decode('utf-8')}\")\n# Expected: {\"item\":{\"_type\":\"CustomObject\",\"custom_value\":\"hello\"}}","lang":"python","description":"Demonstrates basic usage of `encode_canonical_json` and how to register a preserialization callback for custom Python objects, a feature introduced in version 2.0.0 to handle types not natively supported by standard JSON serialization. Keys in objects are sorted, and whitespace is removed for canonical form."},"warnings":[{"fix":"Remove any calls to `canonicaljson.set_json_library()`. Ensure your environment and data are compatible with the standard `json` module. If you relied on `simplejson`-specific behaviors, you may need to adjust your data or provide custom serialization hooks.","message":"Version 2.0.0 removed direct support for `simplejson` and the `set_json_library` function. The library now relies solely on the standard library's `json` module. Code that explicitly configured `simplejson` or relied on its default use will break.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"For `frozendict` or any other custom types, use `canonicaljson.register_preserialisation_callback(YourType, your_serializer_function)` to define how these objects should be serialized into a JSON-encodable structure. Alternatively, convert `frozendict` instances to standard `dict`s before encoding.","message":"Version 2.0.0 removed built-in support for serializing `frozendict` instances directly. If your data structures included `frozendict`, they will no longer be automatically converted to canonical JSON.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Before passing data to `encode_canonical_json`, ensure all `float('inf')`, `float('-inf')`, and `float('nan')` values are converted to `null` or a string representation (e.g., `\"Infinity\"`) that your consuming applications can interpret. The library focuses on canonical representation of *valid* JSON.","message":"Version 1.4.0 introduced stricter compliance with RFC 7159 regarding floating-point numbers, specifically `Infinity`, `-Infinity`, and `NaN`. Previously, these might have been encoded in a non-standard way. Now, encoding objects containing these values directly will likely cause `TypeError` as they are not valid JSON numbers.","severity":"breaking","affected_versions":">=1.4.0"},{"fix":"For critical cryptographic applications, consider representing high-precision numbers or very large integers as strings within your JSON data to avoid potential floating-point representation issues. Define a clear convention for such string representations.","message":"While `canonicaljson` ensures consistent output, floating-point numbers in JSON are based on IEEE 754 double-precision, which can lead to precision loss for very large integers or specific decimal representations. This can potentially cause different systems to derive different canonical forms if they handle number precision differently before serialization.","severity":"gotcha","affected_versions":"All"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"For `canonicaljson >= 2.0.0`, register a preserialization callback: `from canonicaljson import register_preserialisation_callback; register_preserialisation_callback(frozendict, lambda obj: dict(obj))`. Alternatively, manually convert `frozendict` to `dict` before encoding.","cause":"Attempting to serialize a `frozendict` instance directly with `canonicaljson` version 2.0.0 or later, without registering a custom serialization callback. Older versions of `canonicaljson` (pre-2.0.0) might have handled it, but v2.0.0 removed this built-in support.","error":"TypeError: Object of type frozendict is not JSON serializable"},{"fix":"Define and register a `preserialisation_callback` for your custom type: `from canonicaljson import register_preserialisation_callback; class MyCustomClass: ...; def my_serializer(obj: MyCustomClass) -> dict: return {'_type': 'MyCustomClass', 'value': obj.some_attribute}; register_preserialisation_callback(MyCustomClass, my_serializer)`.","cause":"You are trying to serialize a custom Python object type that `canonicaljson` (and the underlying `json` module) doesn't know how to convert into a JSON primitive (like string, number, dict, list, boolean, null).","error":"TypeError: Object of type <YourCustomType> is not JSON serializable"},{"fix":"Before passing your data to `canonicaljson.encode_canonical_json()`, replace these special float values with JSON-compatible alternatives, such as `None` (which serializes to `null`), or a descriptive string (e.g., `\"Infinity\"`, `\"NaN\"`). Example: `data = {k: None if isinstance(v, float) and (v == float('inf') or v == float('-inf') or v != v) else v for k, v in data.items()}`.","cause":"You are attempting to serialize a Python `float('inf')`, `float('-inf')`, or `float('nan')` directly into JSON. These are not valid numeric values in the JSON specification (RFC 8259), and `canonicaljson` enforces this compliance, especially since v1.4.0.","error":"ValueError: Invalid float value for JSON encoding: Infinity"}]}