Part 3 — Serialisation¶
ndict-tools provides built-in serialisation to JSON and pickle for
all three public classes. Both formats support a full round-trip: the
reconstructed object is an instance of the same class with the same
default_setup.
Warning
Pickle files are unsafe when loaded from untrusted sources.
Only unpickle files you created yourself or received from a trusted
party. A UserWarning is always emitted on both to_pickle
and from_pickle to remind you of this.
JSON round-trip¶
Use to_json() to write and
from_json() to reconstruct:
from ndict_tools import NestedDictionary
nd = NestedDictionary({"project": {"name": "ndict-tools", "stars": 42}})
nd.to_json("/tmp/nd.json", indent=2)
nd2 = NestedDictionary.from_json(
"/tmp/nd.json",
default_setup={"indent": 2, "default_factory": NestedDictionary},
)
nd2[["project", "name"]] # 'ndict-tools'
The default_setup parameter in from_json is required for the same
reason as in from_dict() — the JSON
file carries no information about the target class behaviour.
Non-string keys¶
JSON mandates string keys. ndict-tools encodes all non-string keys automatically using a reversible convention (DD-021) — you do not need to handle this yourself:
nd = NestedDictionary({42: {"label": "answer"}, "name": "demo"})
nd.to_json("/tmp/mixed.json")
nd2 = NestedDictionary.from_json(
"/tmp/mixed.json",
default_setup={"indent": 0, "default_factory": NestedDictionary},
)
nd2[42]["label"] # 'answer' — integer key restored correctly
Supported key types for JSON encoding: str, int,
float, bool, flat tuple, flat frozenset.
Pickle round-trip¶
Use to_pickle() and
from_pickle():
from ndict_tools import NestedDictionary
nd = NestedDictionary({"a": {1: "one", 2: "two"}})
nd.to_pickle("/tmp/nd.pkl")
# Writes /tmp/nd.pkl and /tmp/nd.pkl.sha256 (SHA-256 integrity sidecar)
nd2 = NestedDictionary.from_pickle("/tmp/nd.pkl")
# Verifies the SHA-256 sidecar before unpickling
nd2["a"][1] # 'one'
A .sha256 sidecar file is written alongside the pickle file. By
default, from_pickle reads the sidecar and raises
StackedValueError if the digest does not match or
the sidecar is absent. To skip the check:
nd2 = NestedDictionary.from_pickle("/tmp/nd.pkl", verify=False)
Pickle preserves the full object state, including default_setup and
default_factory, without any extra arguments on load.
JSON vs pickle — when to use which¶
JSON |
Pickle |
|
|---|---|---|
Key types |
|
Any hashable type |
Values |
JSON-serialisable only (str, int, float, bool, list, dict, None) |
Any picklable Python object |
Interoperability |
Any language, any tool |
Python only |
Readability |
Human-readable text |
Binary |
Security |
Safe to load from untrusted sources |
Never load from untrusted sources |
Integrity check |
None built-in |
SHA-256 sidecar (automatic) |
``default_setup`` on load |
Required |
Preserved automatically |
Rule of thumb: use JSON for configuration files, data exchange, and anything that crosses a trust boundary. Use pickle for local persistence of complex objects (non-string keys, custom value types) within a trusted environment.