feat(stdlib): make json_encode strict and add json_encode_lossy 🧊 - #199
Conversation
json_encode now only accepts values that survive an exact round trip through json_decode and errors on everything else (rationals, complex, options, tuples, deques, iterators, heaps, sets, unit, non-string map keys, map defaults, non-finite floats, cyclic values). The new json_encode_lossy accepts those by degrading them. json_decode now maps null to None and decodes big integers exactly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Structs are rejected by the strict json_encode (a JSON object decodes back to a map, not a struct) and encode as objects via json_encode_lossy. The serde.rs arms landed while resolving the rebase onto #199. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f7bb0acecf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…_lossy 🌀 Found by review on #199: a heap pushed into itself overflowed the stack because only lists, deques and maps were in the cycle guard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Structs are rejected by the strict json_encode (a JSON object decodes back to a map, not a struct) and encode as objects via json_encode_lossy. The serde.rs arms landed while resolving the rebase onto #199. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e7122431f3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
() now converts to null and null decodes to (), while None joins Some as a rejected option value in strict json_encode. As a consequence string-keyed sets round-trip exactly (as objects with null values), so they are accepted again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Structs are rejected by the strict json_encode (a JSON object decodes back to a map, not a struct) and encode as objects via json_encode_lossy. The serde.rs arms landed while resolving the rebase onto #199. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Structs are rejected by the strict json_encode (a JSON object decodes back to a map, not a struct) and encode as objects via json_encode_lossy. The serde.rs arms landed while resolving the rebase onto #199. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Context
While working on struct support in #197 (the
to_jsonTODO), we decided JSON conversion should be perfect in both directions:json_encodemust be the exact inverse ofjson_decodeand error whenever that's impossible, instead of silently degrading values. Extracted as a precursor PR off master so #197 can rebase on it and add only the struct arm.Changes
json_encodeis now strict. It only accepts values for whichjson_decode(json_encode(v)) == vholds: the unit value(), booleans, ints, big ints, finite floats, strings, lists, string-keyed sets, and maps with string keys. Everything else errors with a hint: rationals, complex numbers, non-finite floats, options (bothSomeandNone), tuples, deques, iterators, heaps, functions, maps with non-string keys or a default value, and cyclic values.json_encode_lossyaccepts the rejected values by degrading them: rationals → floats, complex → strings,Some(x)unwrapped tox,None→null, tuples/deques → arrays, heaps → arrays in priority order (deterministic, was arbitrary internal heap order), iterators drained, non-string keys stringified, map defaults dropped, non-finite floats →null. It has no decode counterpart because these conversions cannot be reversed. Functions and cyclic values still error.json_decodefixes: integers beyondi64decode exactly to big ints via serde_json'sarbitrary_precision(previously silently lossy throughf64), and numbers that overflow a float (1e999) error.json_encode([l where l contains l])used to crash the process, now it's an error.Notes for reviewers
()↔nullin both directions;Noneis rejected along withSomeso the pairing stays unambiguous (previously bothNoneand()encoded tonull).()values, sets are automatically accepted and round-trip exactly as objects with all-nullvalues:%{"a"}↔{"a":null}.List, so the round trip doesn't hold.ndc docs); there's no manual page for JSON yet.🤖