Skip to content

feat: encode and decode JSON through the runtime codec - #2665

Merged
mromaszewicz merged 4 commits into
oapi-codegen:mainfrom
CRaLFa:use-runtime-json-codec
Nov 11, 2026
Merged

mromaszewicz merged 4 commits into
oapi-codegen:mainfrom
CRaLFa:use-runtime-json-codec

Conversation

@CRaLFa

@CRaLFa CRaLFa commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #2371, replacing it. Generated code now goes through the process-wide JSON codec added in runtime v1.9.0 (oapi-codegen/runtime#183), so runtime.SetJSONCodec applies to it.

This follows the split described in #2371 (comment).

Changes

  • Byte codec: every json.Marshal and json.Unmarshal in the templates becomes runtime.JSONMarshal and runtime.JSONUnmarshal. That covers the model code (additional-properties.tmpl, union.tmpl, union-and-additional-properties.tmpl), client request bodies and JSON-valued parameters, client response parsing (template_helpers.go), and server JSON-valued parameters for every framework.
  • Streaming codec: strict responses in strict/strict-interface.tmpl encode with runtime.JSONMarshalWrite(&buf, ...). The intermediate buffer and the ordering around WriteHeader are unchanged.
  • Left on encoding/json: the json.NewDecoder(...).UseNumber() probe in the union UnmarshalText, and json.Valid, json.RawMessage and json.Number.
  • Out of scope: request body binding and ctx.JSON in the echo, gin, fiber and iris strict servers.
  • The runtime dependency of examples and internal/test is bumped to v1.9.0.
  • TestContentTypesShortName expected json.Unmarshal(bodyBytes, &dest) in the generated code; it now expects the runtime call.

Fixtures

Regenerated with make generate on Go 1.27. Every changed line in the generated files is one of the replaced calls, a dropped encoding/json import or an added runtime import. The embedded spec blobs are untouched.

The import lists needed no change: imports.tmpl already lists both packages, and imports.Process drops the unused one.

Tests

Each kind of call site runs with a codec set through runtime.SetJSONCodec. The tests extend existing fixtures, and none of them is parallel, since the codec is process-wide.

  • servers/strict/stdhttp: one JSON round trip through the generated client and the strict server reaches the codec at the client request body, the server request binding, the server response and the client response parsing. A codec implementing runtime.StreamingJSONCodec gets the request body through UnmarshalRead and the response through MarshalWrite, with its byte methods not called.
  • schemas/objects: MarshalJSON / UnmarshalJSON of an additionalProperties type, called through the codec so that they re-enter it.
  • aggregates/oneof: a union's From* / As* / Merge*, and MarshalJSON / UnmarshalJSON of a union with additionalProperties.
  • parameters/roundtrip: JSON-valued path, query, header and cookie parameters, one client marshal and one server unmarshal each, for every framework in the shared testImpl.

Docs

  • docs/stdhttp-server.md says Go 1.26, which runtime v1.9.0 requires.
  • The additionalProperties, anyOf and oneOf samples in the README show runtime.JSONMarshal and runtime.JSONUnmarshal.

Verification

On Go 1.27: make test, make lint and make tidy-ci pass.

🤖 Generated with Claude Code

Generated code now goes through the process-wide JSON codec added in runtime v1.9.0, so runtime.SetJSONCodec applies to it.

- Replace json.Marshal and json.Unmarshal in the templates with runtime.JSONMarshal and runtime.JSONUnmarshal.
- Encode strict server responses with runtime.JSONMarshalWrite, keeping the intermediate buffer.
- Leave the json.NewDecoder probe in the union UnmarshalText on encoding/json, since it depends on UseNumber.
- Bump the runtime dependency of examples and internal/test to v1.9.0.
- Regenerate the fixtures with Go 1.27.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@CRaLFa
CRaLFa requested a review from a team as a code owner November 11, 2026 02:43
@socket-security

socket-security Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedgithub.com/​oapi-codegen/​runtime@​v1.8.0 ⏵ v1.9.094 +1100100100100

View full report

@greptile-apps

greptile-apps Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[High impact] The PR appears safe to merge; no new actionable issue was found.

Summary

Generated JSON calls now use the runtime codec. The latest changes add broader codec tests and update documentation.

  • Generated models and API calls now use the process-wide JSON codec.

Reviews (3) · Last reviewed commit: "docs: update generated-code samples and ..." · Reviewed by Greptile

Comment thread pkg/codegen/templates/client.tmpl Resolved
Comment thread pkg/codegen/content_types_test.go Resolved
Add a round trip through the generated client and the strict stdhttp server with a counting codec installed.
The existing tests only check the generated text, so they cannot tell the codec from a direct call to encoding/json.

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

mromaszewicz commented Nov 11, 2026 •

Copy link
Copy Markdown
Member

Thanks @CRaLFa, this follows the split exactly: every call site is switched, the strict response keeps its buffer, and the regenerated files contain nothing but the swap and the import changes. Two things I'd like addressed before merging.

1. The test doesn't cover enough

TestJSONCodec exercises the four body steps of one round trip, which is a good start, but three kinds of call site never run with a custom codec installed:

  • Model methods. The MarshalJSON / UnmarshalJSON of an additionalProperties type, and a union's As* / From* / Merge* and its own MarshalJSON / UnmarshalJSON. These are the re-entrant case, since they call back into the codec from inside the codec's own Marshal, and that is the part of the contract most likely to break.
  • JSON-valued parameters (content: application/json): the client-side marshal and the server-side unmarshal. internal/test/parameters/roundtrip/ already has parameters like this.
  • The streaming half. The codec in the test is byte-only, so it can't tell runtime.JSONMarshalWrite from runtime.JSONMarshal at the strict response. A codec that implements runtime.StreamingJSONCodec should receive the strict response through MarshalWrite and the request body through UnmarshalRead, with its byte methods not called for either.

Please extend existing fixtures that already have the right types where you can, in the category that matches the feature, rather than adding new specs. Since the codec is process-wide, none of these tests may call t.Parallel.

2. Documentation that is now wrong

  • docs/stdhttp-server.md (around line 143) says the go directive should be at least go 1.24 because the runtime requires Go 1.24+. Generated code now requires runtime v1.9.0, which requires Go 1.26.
  • The generated-code samples in the README still show the old output: the "Boilerplate" blocks under the three additionalProperties sections, and the anyOf and oneOf samples under "How does oapi-codegen handle anyOf, allOf and oneOf?". They should show runtime.JSONMarshal and runtime.JSONUnmarshal, as the generator now emits.

The json.NewEncoder(w).Encode(...) lines in the README introduction and in the chi, gorilla and stdhttp server docs are hand-written handler code, so those stay as they are.

CRaLFa and others added 2 commits November 11, 2026 13:46
…a custom codec

Run the remaining kinds of call site with a codec set through runtime.SetJSONCodec.

- schemas/objects: MarshalJSON and UnmarshalJSON of an additionalProperties type.
- aggregates/oneof: From*, As*, Merge*, MarshalJSON and UnmarshalJSON of a union, with and without additionalProperties.
- parameters/roundtrip: JSON-valued path, query, header and cookie parameters, client and server, for every framework.
- servers/strict/stdhttp: a codec implementing runtime.StreamingJSONCodec receives the request body and the response as streams.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
….9.0

- Show runtime.JSONMarshal and runtime.JSONUnmarshal in the additionalProperties, anyOf and oneOf samples of the README.
- Raise the go directive in docs/stdhttp-server.md to 1.26, which runtime v1.9.0 requires.

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

CRaLFa commented Nov 11, 2026

Copy link
Copy Markdown
Contributor Author

@mromaszewicz Thanks for the review. Both points are addressed.

Tests (9ab50ad), extending existing fixtures, none of them parallel:

  • schemas/objects: MarshalJSON / UnmarshalJSON of an additionalProperties type, called through the codec so that they re-enter it.
  • aggregates/oneof: a union's From* / As* / Merge*, and MarshalJSON / UnmarshalJSON of a union with additionalProperties.
  • parameters/roundtrip: JSON-valued path, query, header and cookie parameters. It sits in the shared testImpl, so it runs for every framework there, and asserts one client marshal and one server unmarshal each.
  • servers/strict/stdhttp: a codec implementing runtime.StreamingJSONCodec gets the request body through UnmarshalRead and the response through MarshalWrite, with its byte methods not called.

Docs (1f2d3e1): docs/stdhttp-server.md now says Go 1.26, and the additionalProperties, anyOf and oneOf samples in the README show the runtime calls.

One question: the server table in the README lists 1.24+ as the Go version needed to compile the generated code for chi, echo, fiber, gorilla, iris and net/http, and 1.25+ for the others. With runtime v1.9.0 that is 1.26 for all of them. I left the table alone since you didn't mention it; should I update it here?

@mromaszewicz

Copy link
Copy Markdown
Member

Thanks for doing this, and thanks for being flexible in addressing changes.

@mromaszewicz
mromaszewicz merged commit 045420f into oapi-codegen:main Nov 11, 2026
14 checks passed
mromaszewicz added a commit that referenced this pull request Nov 11, 2026
* chore: fix malformed Greptile config and duplicate rule number

.greptile/config.json began with two opening braces, so it has not been
valid JSON since it was last edited, and the strictness and *.gen.go
settings in it could not be parsed. Remove the extra brace.

.greptile/rules.md had two sections numbered 5. The schema-merging rule,
which comes last, becomes section 8.

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

* chore: add a Greptile rule that generated JSON uses the runtime codec

Generated code is moving to the process-wide JSON codec in runtime
v1.9.0 (#2665), so that runtime.SetJSONCodec applies to all of it. A
template that calls encoding/json directly escapes the codec without any
test noticing, because the default codec is encoding/json.

Add a review rule that flags direct json.Marshal, json.Unmarshal,
json.NewEncoder and json.NewDecoder calls in templates and in code that
emits generated source, names the runtime function to use for each, and
protects the buffer-before-WriteHeader ordering of the strict response.
It lists what is exempt: json.RawMessage, json.Number, json.Valid, the
UseNumber probe in the union template, JSON a web framework handles, and
the generator's own use of encoding/json.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
mromaszewicz added a commit that referenced this pull request Nov 11, 2026
Generated code imports github.com/oapi-codegen/runtime, and with the
JSON codec change (#2665) it needs runtime v1.9.0, which requires Go
1.26. The README still listed Go 1.24 or 1.25 per server and said the
generated code has lower requirements than oapi-codegen itself.

Set every row of the Supported Servers table to 1.26+, and reword the
table's introduction and the note under Installing to say that the
generated code has the same Go 1.26 floor and why.

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants