Skip to content

Commit e2f7dfd

Browse files
lzapjamietanna
authored andcommitted
feat(extensions): add support for x-omitzero
One of the big improvements added in Go 1.24 is `omitzero`[0][1][2], which makes it possible to omit the "zero value" of a given Go type when marshaling to JSON, or if the `IsZero() bool` function on the type returns `true`. To make it possible for `oapi-codegen` to use this, we can wire in a new extension, `x-omitzero` which allows per-field tuning of this behaviour. This is something that is safe to generate alongside the `omitempty` tags[3]: > If both "omitempty" and "omitzero" are specified, the field will be omitted if the value is either empty or zero (or both). To validate this, we can add a new example for the extension, which provides testing of the expected behaviour. As this requires Go 1.24, we need to set up the boilerplate for a new module that only runs Go 1.24. Closes oapi-codegen#1899. [0]: https://www.jvt.me/posts/2025/02/12/go-omitzero-124/ [1]: https://www.bytesizego.com/blog/go-124-omitzero [2]: https://antonz.org/go-1-24/#omit-zero-values-in-json [3]: https://pkg.go.dev/encoding/json
1 parent 001735e commit e2f7dfd

13 files changed

Lines changed: 702 additions & 19 deletions

File tree

README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2324,6 +2324,17 @@ Force the presence of the JSON tag `omitempty` on a field
23242324
<tr>
23252325
<td>
23262326

2327+
`x-omitzero`
2328+
2329+
</td>
2330+
<td>
2331+
Force the presence of the JSON tag `omitzero` on a field
2332+
</td>
2333+
</tr>
2334+
2335+
<tr>
2336+
<td>
2337+
23272338
`x-go-json-ignore`
23282339

23292340
</td>
@@ -2678,6 +2689,61 @@ Notice that the `ComplexField` is still generated in full, but the type will the
26782689

26792690
You can see this in more detail in [the example code](examples/extensions/xomitempty/).
26802691

2692+
### `x-omitzero` - force the presence of the JSON tag `omitzero` on a field
2693+
2694+
> [!NOTE]
2695+
> `omitzero` was added in Go 1.24. If you're not using Go 1.24 in your project, this won't work.
2696+
2697+
In a case that you may want to add the JSON struct tag `omitzero` to types, you can use the `x-omitempty` extension.
2698+
2699+
We can see this at play with the following schemas:
2700+
2701+
```yaml
2702+
openapi: "3.0.0"
2703+
info:
2704+
version: 1.0.0
2705+
title: x-omitempty
2706+
components:
2707+
schemas:
2708+
Client:
2709+
type: object
2710+
required:
2711+
- name
2712+
properties:
2713+
name:
2714+
type: string
2715+
id:
2716+
type: number
2717+
ClientWithExtension:
2718+
type: object
2719+
required:
2720+
- name
2721+
properties:
2722+
name:
2723+
type: string
2724+
id:
2725+
type: number
2726+
x-omitzero: true
2727+
```
2728+
2729+
From here, we now get two different models:
2730+
2731+
```go
2732+
// Client defines model for Client.
2733+
type Client struct {
2734+
Id *float32 `json:"id,omitempty"`
2735+
Name string `json:"name"`
2736+
}
2737+
2738+
// ClientWithExtension defines model for ClientWithExtension.
2739+
type ClientWithExtension struct {
2740+
Id *float32 `json:"id,omitempty,omitzero"`
2741+
Name string `json:"name"`
2742+
}
2743+
```
2744+
2745+
You can see this in more detail in [the example code](examples/extensions/xomitzero/).
2746+
26812747
### `x-go-json-ignore` - when (un)marshaling JSON, ignore field(s)
26822748

26832749
By default, `oapi-codegen` will generate `json:"..."` struct tags for all fields in a struct, so JSON (un)marshaling works.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
SHELL:=/bin/bash
2+
3+
YELLOW := \e[0;33m
4+
RESET := \e[0;0m
5+
6+
GOVER := $(shell go env GOVERSION)
7+
GOMINOR := $(shell bash -c "cut -f2 -d. <<< $(GOVER)")
8+
9+
define execute-if-go-124
10+
@{ \
11+
if [[ 24 -le $(GOMINOR) ]]; then \
12+
$1; \
13+
else \
14+
echo -e "$(YELLOW)Skipping task as you're running Go v1.$(GOMINOR).x which is < Go 1.24, which this module requires$(RESET)"; \
15+
fi \
16+
}
17+
endef
18+
19+
lint:
20+
$(call execute-if-go-124,$(GOBIN)/golangci-lint run ./...)
21+
22+
lint-ci:
23+
24+
$(call execute-if-go-124,$(GOBIN)/golangci-lint run ./... --output.text.path=stdout --timeout=5m)
25+
26+
generate:
27+
$(call execute-if-go-124,go generate ./...)
28+
29+
test:
30+
$(call execute-if-go-124,go test -cover ./...)
31+
32+
tidy:
33+
$(call execute-if-go-124,go mod tidy)
34+
35+
tidy-ci:
36+
$(call execute-if-go-124,tidied -verbose)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
openapi: "3.0.0"
2+
info:
3+
version: 1.0.0
4+
title: x-omitempty
5+
components:
6+
schemas:
7+
Client:
8+
type: object
9+
required:
10+
- name
11+
properties:
12+
name:
13+
type: string
14+
id:
15+
type: number
16+
ClientWithExtension:
17+
type: object
18+
required:
19+
- name
20+
properties:
21+
name:
22+
type: string
23+
id:
24+
type: number
25+
x-omitzero: true
26+
ContainerTypeWithRequired:
27+
type: object
28+
properties:
29+
has_is_zero:
30+
$ref: "#/components/schemas/FieldWithCustomIsZeroMethod"
31+
required:
32+
- has_is_zero
33+
ContainerTypeWithOptional:
34+
type: object
35+
properties:
36+
has_is_zero:
37+
$ref: "#/components/schemas/FieldWithCustomIsZeroMethod"
38+
FieldWithCustomIsZeroMethod:
39+
type: object
40+
properties:
41+
id:
42+
type: string
43+
value:
44+
type: number
45+
x-omitzero: true
46+
FieldWithOmitZeroOnRequiredField:
47+
type: object
48+
properties:
49+
id:
50+
type: string
51+
x-omitzero: true
52+
required:
53+
- id
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# yaml-language-server: $schema=../../../configuration-schema.json
2+
package: xomitzero
3+
output: types.gen.go
4+
generate:
5+
models: true
6+
output-options:
7+
# to make sure that all types are generated, even if they're unreferenced
8+
skip-prune: true
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package xomitzero
2+
3+
//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config cfg.yaml api.yaml
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
module github.com/oapi-codegen/oapi-codegen/v2/examples/extensions/xomitzero
2+
3+
go 1.24
4+
5+
replace github.com/oapi-codegen/oapi-codegen/v2 => ../../../
6+
7+
require (
8+
github.com/oapi-codegen/oapi-codegen/v2 v2.0.0-00010101000000-000000000000
9+
github.com/stretchr/testify v1.10.0
10+
)
11+
12+
require (
13+
github.com/davecgh/go-spew v1.1.1 // indirect
14+
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
15+
github.com/getkin/kin-openapi v0.128.0 // indirect
16+
github.com/go-openapi/jsonpointer v0.21.0 // indirect
17+
github.com/go-openapi/swag v0.23.0 // indirect
18+
github.com/invopop/yaml v0.3.1 // indirect
19+
github.com/josharian/intern v1.0.0 // indirect
20+
github.com/mailru/easyjson v0.7.7 // indirect
21+
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
22+
github.com/perimeterx/marshmallow v1.1.5 // indirect
23+
github.com/pmezard/go-difflib v1.0.0 // indirect
24+
github.com/speakeasy-api/jsonpath v0.6.0 // indirect
25+
github.com/speakeasy-api/openapi-overlay v0.10.2 // indirect
26+
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
27+
golang.org/x/mod v0.17.0 // indirect
28+
golang.org/x/text v0.20.0 // indirect
29+
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
30+
gopkg.in/yaml.v2 v2.4.0 // indirect
31+
gopkg.in/yaml.v3 v3.0.1 // indirect
32+
)

0 commit comments

Comments
 (0)