Skip to content

gcvctor: marshal string Values in JSONFromNullable/PGJSONBFromNullable like the client#237

Merged
apstndb merged 1 commit into
mainfrom
gcvctor-json-fromnullable-marshal
Jun 11, 2026
Merged

gcvctor: marshal string Values in JSONFromNullable/PGJSONBFromNullable like the client#237
apstndb merged 1 commit into
mainfrom
gcvctor-json-fromnullable-marshal

Conversation

@apstndb

@apstndb apstndb commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Closes #236. Feedback from the spanenc adoption of v0.7.2 (#232 / #207).

What

JSONFromNullable and PGJSONBFromNullable no longer special-case a string Value as pre-encoded wire JSON. They always delegate to JSONValue / PGJSONBValue, so a Go string marshals to a quoted JSON string on the wire — the same semantics as the official client's encodeValue. Pre-encoded wire JSON is passed as json.RawMessage, which jsonWireString's json.Encoder already stores as-is (validated and compacted, no HTML escaping); the client follows the same json.Marshal convention for RawMessage, keeping the two aligned.

Input Value v0.7.2 wire this PR wire client wire
"x" (string) x (invalid JSON) "x" "x"
json.RawMessage("{\"a\":1}") {"a":1} (via marshal) {"a":1} {"a":1}

Why this is a bug fix, not a design change

These helpers take the client's own wrapper types, whose Value semantics the client defines: a client-decoded JSON string column yields a Go string in NullJSON.Value, and re-encoding it through the v0.7.2 helpers corrupts the round trip ("x" becomes bare x, which is not valid JSON wire). Interpreting client-typed input differently from the client is a contract violation, and the output is malformed — the same class as the v0.7.1 wire-correctness fixes (TimestampValue UTC normalization, JSON HTML-escape removal), which shipped as a patch.

The divergence is also what kept spanenc on its hand-rolled encodeNullJSON / encodePGJsonB (apstndb/spanenc@662f7a8). With this fix spanenc can adopt these helpers. json.RawMessage is the established encoding/json idiom for pre-encoded payloads, so the double-marshal-avoidance use case keeps an explicit, client-compatible spelling.

Changes

  • gcvctor/nullable_input.go: drop the string special-case in both helpers; document the marshal semantics and the RawMessage path.
  • gcvctor/doc.go: note the behavior in the nullable-inputs section.
  • gcvctor/example_test.go: ExampleJSONFromNullable pinning string-vs-RawMessage wire output.
  • gcvctor/nullable_input_test.go: update string-Value expectations to quoted wire; add RawMessage pass-through and invalid-RawMessage error cases.

Release note

Recommend shipping as a patch (v0.7.3): it fixes invalid wire output from a helper introduced in v0.7.2 (released 2026-06-11) with no known adopters — spanenc, the requester of #232, explicitly declined the special case. Suggested release-notes row:

Area v0.7.2 v0.7.3
JSONFromNullable / PGJSONBFromNullable with string Value Wire as-is (invalid for non-JSON strings) Quoted JSON string (client-compatible); use json.RawMessage for as-is

🤖 Generated with Claude Code

#236)

Drop the string special-case so both helpers always delegate to
JSONValue/PGJSONBValue, matching the official client encodeValue: a Go
string Value becomes a quoted JSON string on the wire. Pre-encoded wire
JSON is passed as json.RawMessage, which jsonWireString already stores
as-is (validated and compacted), the same convention the client follows.

Closes #236.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates JSONFromNullable and PGJSONBFromNullable to marshal Go strings as quoted JSON strings, aligning with the official Spanner client's encodeValue behavior. To store pre-encoded wire JSON as-is, users must now pass it as json.RawMessage. The PR also updates the documentation, adds examples, and adjusts the unit tests to reflect and verify this behavior. I have no feedback to provide as the changes are clean, well-documented, and properly tested.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@apstndb apstndb merged commit 77bc274 into main Jun 11, 2026
5 checks passed
@apstndb apstndb deleted the gcvctor-json-fromnullable-marshal branch June 11, 2026 02:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gcvctor: JSONFromNullable/PGJSONBFromNullable string special-case diverges from the client; prefer json.RawMessage for pre-encoded wire

1 participant