diff --git a/README.md b/README.md
index ab01d54e0b..4213e10a26 100644
--- a/README.md
+++ b/README.md
@@ -2743,6 +2743,63 @@ You can see this in more detail in [the example code](examples/extensions/xorder
+
+|
+
+`x-oapi-codegen-only-honour-go-name`
+
+ |
+
+Only honour the `x-go-name` when generating field names
+ |
+
+
+
+> [!WARNING]
+> Using this option may lead to cases where `oapi-codegen`'s rewriting of field names to prevent clashes with other types, or to prevent including characters that may not be valid Go field names.
+
+In some cases, you may not want use the inbuilt options for converting an OpenAPI field name to a Go field name, such as the `name-normalizer: "ToCamelCaseWithInitialisms"`, and instead trust the name that you've defined for the type better.
+
+In this case, you can use `x-oapi-codegen-only-honour-go-name` to enforce this, alongside specifying the `allow-unexported-struct-field-names` compatibility option.
+
+This allows you to take a spec such as:
+
+```yaml
+openapi: "3.0.0"
+info:
+ version: 1.0.0
+ title: x-oapi-codegen-only-honour-go-name
+components:
+ schemas:
+ TypeWithUnexportedField:
+ description: A struct will be output where one of the fields is not exported
+ properties:
+ name:
+ type: string
+ id:
+ type: string
+ # NOTE that there is an explicit usage of a lowercase character
+ x-go-name: accountIdentifier
+ x-oapi-codegen-extra-tags:
+ json: "-"
+ x-oapi-codegen-only-honour-go-name: true
+```
+
+And we'll generate:
+
+```go
+// TypeWithUnexportedField A struct will be output where one of the fields is not exported
+type TypeWithUnexportedField struct {
+ accountIdentifier *string `json:"-"`
+ Name *string `json:"name,omitempty"`
+}
+```
+
+You can see this in more detail in [the example code](examples/extensions/xoapicodegenonlyhonourgoname).
+
+
+ |
+
diff --git a/configuration-schema.json b/configuration-schema.json
index ce346d92e9..836d48ad61 100644
--- a/configuration-schema.json
+++ b/configuration-schema.json
@@ -99,6 +99,10 @@
"circular-reference-limit": {
"type": "integer",
"description": "DEPRECATED: No longer used.\nCircularReferenceLimit allows controlling the limit for circular reference checking. In some OpenAPI specifications, we have a higher number of circular references than is allowed out-of-the-box, but can be tuned to allow traversing them."
+ },
+ "allow-unexported-struct-field-names": {
+ "type": "boolean",
+ "description": "AllowUnexportedStructFieldNames makes it possible to output structs that have fields that are unexported.\nThis is expected to be used in conjunction with an extension such as `x-go-name` to override the output name, and `x-oapi-codegen-extra-tags` to not produce JSON tags for `encoding/json`.\nNOTE that this can be confusing to users of your OpenAPI specification, who may see a field present and therefore be expecting to see it in the response, without understanding the nuance of how `oapi-codegen` generates the code."
}
}
},
diff --git a/examples/extensions/xoapicodegenonlyhonourgoname/api.yaml b/examples/extensions/xoapicodegenonlyhonourgoname/api.yaml
new file mode 100644
index 0000000000..7a1fc9e3ce
--- /dev/null
+++ b/examples/extensions/xoapicodegenonlyhonourgoname/api.yaml
@@ -0,0 +1,18 @@
+openapi: "3.0.0"
+info:
+ version: 1.0.0
+ title: x-oapi-codegen-only-honour-go-name
+components:
+ schemas:
+ TypeWithUnexportedField:
+ description: A struct will be output where one of the fields is not exported
+ properties:
+ name:
+ type: string
+ id:
+ type: string
+ # NOTE that there is an explicit usage of a lowercase character
+ x-go-name: accountIdentifier
+ x-oapi-codegen-extra-tags:
+ json: "-"
+ x-oapi-codegen-only-honour-go-name: true
diff --git a/examples/extensions/xoapicodegenonlyhonourgoname/cfg.yaml b/examples/extensions/xoapicodegenonlyhonourgoname/cfg.yaml
new file mode 100644
index 0000000000..222b37e2ef
--- /dev/null
+++ b/examples/extensions/xoapicodegenonlyhonourgoname/cfg.yaml
@@ -0,0 +1,10 @@
+# yaml-language-server: $schema=../../../configuration-schema.json
+package: xoapicodegenonlyhonourgoname
+output: gen.go
+generate:
+ models: true
+output-options:
+ # to make sure that all types are generated, even if they're unreferenced
+ skip-prune: true
+compatibility:
+ allow-unexported-struct-field-names: true
diff --git a/examples/extensions/xoapicodegenonlyhonourgoname/gen.go b/examples/extensions/xoapicodegenonlyhonourgoname/gen.go
new file mode 100644
index 0000000000..71e3cca4b5
--- /dev/null
+++ b/examples/extensions/xoapicodegenonlyhonourgoname/gen.go
@@ -0,0 +1,10 @@
+// Package xoapicodegenonlyhonourgoname provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT.
+package xoapicodegenonlyhonourgoname
+
+// TypeWithUnexportedField A struct will be output where one of the fields is not exported
+type TypeWithUnexportedField struct {
+ accountIdentifier *string `json:"-"`
+ Name *string `json:"name,omitempty"`
+}
diff --git a/examples/extensions/xoapicodegenonlyhonourgoname/gen_test.go b/examples/extensions/xoapicodegenonlyhonourgoname/gen_test.go
new file mode 100644
index 0000000000..3e616bf99c
--- /dev/null
+++ b/examples/extensions/xoapicodegenonlyhonourgoname/gen_test.go
@@ -0,0 +1,18 @@
+package xoapicodegenonlyhonourgoname
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestTypeWithUnexportedField(t *testing.T) {
+ var v TypeWithUnexportedField
+
+ err := json.Unmarshal([]byte(`{"id": "some-id"}`), &v)
+ require.NoError(t, err)
+
+ // this field will never be unmarshaled
+ require.Nil(t, v.accountIdentifier)
+}
diff --git a/examples/extensions/xoapicodegenonlyhonourgoname/generate.go b/examples/extensions/xoapicodegenonlyhonourgoname/generate.go
new file mode 100644
index 0000000000..938d489126
--- /dev/null
+++ b/examples/extensions/xoapicodegenonlyhonourgoname/generate.go
@@ -0,0 +1,3 @@
+package xoapicodegenonlyhonourgoname
+
+//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config cfg.yaml api.yaml
diff --git a/pkg/codegen/configuration.go b/pkg/codegen/configuration.go
index 89c950ae81..c26be2d36f 100644
--- a/pkg/codegen/configuration.go
+++ b/pkg/codegen/configuration.go
@@ -103,6 +103,21 @@ type CompatibilityOptions struct {
// traversing them.
// Deprecated: In kin-openapi v0.126.0 (https://github.com/getkin/kin-openapi/tree/v0.126.0?tab=readme-ov-file#v01260) the Circular Reference Counter functionality was removed, instead resolving all references with backtracking, to avoid needing to provide a limit to reference counts.
CircularReferenceLimit int `yaml:"circular-reference-limit"`
+ // AllowUnexportedStructFieldNames makes it possible to output structs that have fields that are unexported.
+ //
+ // This is expected to be used in conjunction with `x-go-name` and `x-oapi-codegen-only-honour-go-name` to override the resulting output field name, and `x-oapi-codegen-extra-tags` to not produce JSON tags for `encoding/json`, such as:
+ //
+ // ```yaml
+ // id:
+ // type: string
+ // x-go-name: accountIdentifier
+ // x-oapi-codegen-extra-tags:
+ // json: "-"
+ // x-oapi-codegen-only-honour-go-name: true
+ // ```
+ //
+ // NOTE that this can be confusing to users of your OpenAPI specification, who may see a field present and therefore be expecting to see/use it in the request/response, without understanding the nuance of how `oapi-codegen` generates the code.
+ AllowUnexportedStructFieldNames bool `yaml:"allow-unexported-struct-field-names"`
}
// OutputOptions are used to modify the output code in some way.
diff --git a/pkg/codegen/extension.go b/pkg/codegen/extension.go
index 03b726fba7..f5ef5ef4d2 100644
--- a/pkg/codegen/extension.go
+++ b/pkg/codegen/extension.go
@@ -23,6 +23,9 @@ const (
extEnumNames = "x-enumNames"
extDeprecationReason = "x-deprecated-reason"
extOrder = "x-order"
+ // extOapiCodegenOnlyHonourGoName is to be used to explicitly enforce the generation of a field as the `x-go-name` extension has describe it.
+ // This is intended to be used alongside the `allow-unexported-struct-field-names` Compatibility option
+ extOapiCodegenOnlyHonourGoName = "x-oapi-codegen-only-honour-go-name"
)
func extString(extPropValue interface{}) (string, error) {
@@ -100,3 +103,11 @@ func extParseEnumVarNames(extPropValue interface{}) ([]string, error) {
func extParseDeprecationReason(extPropValue interface{}) (string, error) {
return extString(extPropValue)
}
+
+func extParseOapiCodegenOnlyHonourGoName(extPropValue interface{}) (bool, error) {
+ onlyHonourGoName, ok := extPropValue.(bool)
+ if !ok {
+ return false, fmt.Errorf("failed to convert type: %T", extPropValue)
+ }
+ return onlyHonourGoName, nil
+}
diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go
index f734517a9a..3d4bdbbddb 100644
--- a/pkg/codegen/schema.go
+++ b/pkg/codegen/schema.go
@@ -100,6 +100,16 @@ func (p Property) GoFieldName() string {
}
}
+ if globalState.options.Compatibility.AllowUnexportedStructFieldNames {
+ if extension, ok := p.Extensions[extOapiCodegenOnlyHonourGoName]; ok {
+ if extOapiCodegenOnlyHonourGoName, err := extParseOapiCodegenOnlyHonourGoName(extension); err == nil {
+ if extOapiCodegenOnlyHonourGoName {
+ return goFieldName
+ }
+ }
+ }
+ }
+
return SchemaNameToTypeName(goFieldName)
}