fix(json): preserve string map keys - #820
Conversation
| default: | ||
| nk = k.String() | ||
| } | ||
| r[nk] = vv |
There was a problem hiding this comment.
[P2] Reject collisions after coercing map keys
Now that both "x" and :x become "x", a valid VM map containing both silently drops one entry here because r is a Go map[string]any. This is particularly easy to hit in the advertised read-modify-write flow: (assoc (read-json "{\"x\":1}") :x 2) retains two VM keys, but write-json emits only {"x":2}. Please detect a previously emitted nk (and return an error, or otherwise define a deterministic policy) rather than silently losing data.
mparrett
left a comment
There was a problem hiding this comment.
Requesting changes for the unresolved P2 inline finding: key coercion can silently collapse distinct VM map entries (for example, string "x" and keyword :x) into one JSON object key.
nooga
left a comment
There was a problem hiding this comment.
The string-key fix is correct and minimal. Verified locally: the new tests fail on main and pass here; go test ./pkg/rt, go vet, and make check-generated are all clean; and the generated.manifest hash matches the new json.go sha256. The pods JSON encoder (JSONEncodeArgs in transit.go) and js/emit share fromMapValue and pick up the fix with no dependency on the old quoted form.
+1 to @mparrett's P2. The silent drop on a "x" / :x collision is a Go-map artifact rather than JSON semantics (data.json and cheshire would emit duplicate keys instead), and it is a few lines to detect in this loop. Note it was already reachable for other type pairs ({1 1 "1" 2}, {:x 1 'x 2}), so this PR widens the path rather than creating the class; it could equally land as a follow-up if you prefer to keep this PR to the issue's scope.
One small pre-existing thing in the same switch: the default branch still emits reader syntax for the remaining key types, so (write-json {\a 1}) gives {"\\a":1} and a nil key gives "nil". Routing vm.Char through string(rune(k)) here would be a one-liner if you want to sweep it in.
Summary
Fix
json/write-jsoncorrupting VM string map keys by serializing their underlying text instead of their readable EDN representation.Closes #817.
Reproduction
Before:
After:
Validation
go test ./test -run 'TestRunner/json_test\.lg$' -count=1go test -race ./test -run 'TestRunner/json_test\.lg$' -count=1go test ./pkg/rt -count=1go vet ./pkg/rt ./testmake generatemake check-generatedgit diff --checkA broader
go test ./pkg/rt ./test -count=1pass before the final mechanical restack hadpkg/rtgreen; thetestpackage was blocked only by this sandbox denying the suite's existing literal/tmpwrites. The focused JSON suite is green before and after the restack; CI remains the unrestricted full-suite gate.