Repository: ogen-go/ogen
Pull request #383 (2022-06-07 07:50:56)

feat(gen): nullable oneOf


Patch:
diff --git a/examples/ex_github/oas_faker_gen.go b/examples/ex_github/oas_faker_gen.go
index 747a40a1..e54efdfb 100644
--- a/examples/ex_github/oas_faker_gen.go
+++ b/examples/ex_github/oas_faker_gen.go
@@ -36646,12 +36646,12 @@ func (s *ValidationErrorErrorsItem) SetFake() {
 
 // SetFake set fake values.
 func (s *ValidationErrorErrorsItemValue) SetFake() {
-	var elem NilString
+	var elem struct{}
 
 	{
-		elem.SetFake()
+		elem = struct{}{}
 	}
-	s.SetNilString(elem)
+	s.SetNull(elem)
 }
 
 // SetFake set fake values.
diff --git a/examples/ex_github/oas_json_gen.go b/examples/ex_github/oas_json_gen.go
index 5e83992e..7429a78c 100644
--- a/examples/ex_github/oas_json_gen.go
+++ b/examples/ex_github/oas_json_gen.go
@@ -172149,20 +172149,19 @@ func (s *ValidationErrorErrorsItem) UnmarshalJSON(data []byte) error {
 // Encode encodes ValidationErrorErrorsItemValue as json.
 func (s ValidationErrorErrorsItemValue) Encode(e *jx.Encoder) {
 	switch s.Type {
-	case NilStringValidationErrorErrorsItemValue:
-		s.NilString.Encode(e)
-	case NilIntValidationErrorErrorsItemValue:
-		s.NilInt.Encode(e)
+	case NullValidationErrorErrorsItemValue:
+		_ = s.Null
+		e.Null()
+	case StringValidationErrorErrorsItemValue:
+		e.Str(s.String)
+	case IntValidationErrorErrorsItemValue:
+		e.Int(s.Int)
 	case StringArrayValidationErrorErrorsItemValue:
-		if s.StringArray == nil {
-			e.Null()
-		} else {
-			e.ArrStart()
-			for _, elem := range s.StringArray {
-				e.Str(elem)
-			}
-			e.ArrEnd()
+		e.ArrStart()
+		for _, elem := range s.StringArray {
+			e.Str(elem)
 		}
+		e.ArrEnd()
 	}
 }
 
@@ -172173,36 +172172,38 @@ func (s *ValidationErrorErrorsItemValue) Decode(d *jx.Decoder) error {
 	}
 	// Sum type type_discriminator.
 	switch t := d.Next(); t {
+	case jx.Null:
+		if err := d.Null(); err != nil {
+			return err
+		}
+		s.Type = NullValidationErrorErrorsItemValue
 	case jx.String:
-		if err := s.NilString.Decode(d); err != nil {
+		v, err := d.Str()
+		s.String = string(v)
+		if err != nil {
 			return err
 		}
-		s.Type = NilStringValidationErrorErrorsItemValue
+		s.Type = StringValidationErrorErrorsItemValue
 	case jx.Number:
-		if err := s.NilInt.Decode(d); err != nil {
+		v, err := d.Int()
+		s.Int = int(v)
+		if err != nil {
 			return err
 		}
-		s.Type = NilIntValidationErrorErrorsItemValue
+		s.Type = IntValidationErrorErrorsItemValue
 	case jx.Array:
-		switch tt := d.Next(); tt {
-		case jx.Null:
-			if err := d.Skip(); err != nil {
-				return err
-			}
-		default:
-			s.StringArray = make([]string, 0)
-			if err := d.Arr(func(d *jx.Decoder) error {
-				var elem string
-				v, err := d.Str()
-				elem = string(v)
-				if err != nil {
-					return err
-				}
-				s.StringArray = append(s.StringArray, elem)
-				return nil
-			}); err != nil {
+		s.StringArray = make([]string, 0)
+		if err := d.Arr(func(d *jx.Decoder) error {
+			var elem string
+			v, err := d.Str()
+			elem = string(v)
+			if err != nil {
 				return err
 			}
+			s.StringArray = append(s.StringArray, elem)
+			return nil
+		}); err != nil {
+			return err
 		}
 		s.Type = StringArrayValidationErrorErrorsItemValue
 	default:
diff --git a/examples/ex_github/oas_schemas_gen.go b/examples/ex_github/oas_schemas_gen.go
index 8355e545..1eaa3716 100644
--- a/examples/ex_github/oas_schemas_gen.go
+++ b/examples/ex_github/oas_schemas_gen.go
@@ -40365,8 +40365,9 @@ type ValidationErrorErrorsItem struct {
 // ValidationErrorErrorsItemValue represents sum type.
 type ValidationErrorErrorsItemValue struct {
 	Type        ValidationErrorErrorsItemValueType // switch on this field
-	NilString   NilString
-	NilInt      NilInt
+	Null        struct{}
+	String      string
+	Int         int
 	StringArray []string
 }
 
@@ -40375,19 +40376,25 @@ type ValidationErrorErrorsItemValueType string
 
 // Possible values for ValidationErrorErrorsItemValueType.
 const (
-	NilStringValidationErrorErrorsItemValue   ValidationErrorErrorsItemValueType = "NilString"
-	NilIntValidationErrorErrorsItemValue      ValidationErrorErrorsItemValueType = "NilInt"
+	NullValidationErrorErrorsItemValue        ValidationErrorErrorsItemValueType = "struct{}"
+	StringValidationErrorErrorsItemValue      ValidationErrorErrorsItemValueType = "string"
+	IntValidationErrorErrorsItemValue         ValidationErrorErrorsItemValueType = "int"
 	StringArrayValidationErrorErrorsItemValue ValidationErrorErrorsItemValueType = "[]string"
 )
 
-// IsNilString reports whether ValidationErrorErrorsItemValue is NilString.
-func (s ValidationErrorErrorsItemValue) IsNilString() bool {
-	return s.Type == NilStringValidationErrorErrorsItemValue
+// IsNull reports whether ValidationErrorErrorsItemValue is struct{}.
+func (s ValidationErrorErrorsItemValue) IsNull() bool {
+	return s.Type == NullValidationErrorErrorsItemValue
 }
 
-// IsNilInt reports whether ValidationErrorErrorsItemValue is NilInt.
-func (s ValidationErrorErrorsItemValue) IsNilInt() bool {
-	return s.Type == NilIntValidationErrorErrorsItemValue
+// IsString reports whether ValidationErrorErrorsItemValue is string.
+func (s ValidationErrorErrorsItemValue) IsString() bool {
+	return s.Type == StringValidationErrorErrorsItemValue
+}
+
+// IsInt reports whether ValidationErrorErrorsItemValue is int.
+func (s ValidationErrorErrorsItemValue) IsInt() bool {
+	return s.Type == IntValidationErrorErrorsItemValue
 }
 
 // IsStringArray reports whether ValidationErrorErrorsItemValue is []string.
@@ -40395,45 +40402,66 @@ func (s ValidationErrorErrorsItemValue) IsStringArray() bool {
 	return s.Type == StringArrayValidationErrorErrorsItemValue
 }
 
-// SetNilString sets ValidationErrorErrorsItemValue to NilString.
-func (s *ValidationErrorErrorsItemValue) SetNilString(v NilString) {
-	s.Type = NilStringValidationErrorErrorsItemValue
-	s.NilString = v
+// SetNull sets ValidationErrorErrorsItemValue to struct{}.
+func (s *ValidationErrorErrorsItemValue) SetNull(v struct{}) {
+	s.Type = NullValidationErrorErrorsItemValue
+	s.Null = v
+}
+
+// GetNull returns struct{} and true boolean if ValidationErrorErrorsItemValue is struct{}.
+func (s ValidationErrorErrorsItemValue) GetNull() (v struct{}, ok bool) {
+	if !s.IsNull() {
+		return v, false
+	}
+	return s.Null, true
+}
+
+// NewNullValidationErrorErrorsItemValue returns new ValidationErrorErrorsItemValue from struct{}.
+func NewNullValidationErrorErrorsItemValue(v struct{}) ValidationErrorErrorsItemValue {
+	var s ValidationErrorErrorsItemValue
+	s.SetNull(v)
+	return s
+}
+
+// SetString sets ValidationErrorErrorsItemValue to string.
+func (s *ValidationErrorErrorsItemValue) SetString(v string) {
+	s.Type = StringValidationErrorErrorsItemValue
+	s.String = v
 }
 
-// GetNilString returns NilString and true boolean if ValidationErrorErrorsItemValue is NilString.
-func (s ValidationErrorErrorsItemValue) GetNilString() (v NilString, ok bool) {
-	if !s.IsNilString() {
+// GetString returns string and true boolean if ValidationErrorErrorsItemValue is string.
+func (s ValidationErrorErrorsItemValue) GetString() (v string, ok bool) {
+	if !s.IsString() {
 		return v, false
 	}
-	return s.NilString, true
+	return s.String, true
 }
 
-// NewNilStringValidationErrorErrorsItemValue returns new ValidationErrorErrorsItemValue from NilString.
-func NewNilStringValidationErrorErrorsItemValue(v NilString) ValidationErrorErrorsItemValue {
+// NewStringValidationErrorErrorsItemValue returns new ValidationErrorErrorsItemValue from string.
+func NewStringValidationErrorErrorsItemValue(v string) ValidationErrorErrorsItemValue {
 	var s ValidationErrorErrorsItemValue
-	s.SetNilString(v)
+	s.SetString(v)
 	return s
 }
 
-// SetNilInt sets ValidationErrorErrorsItemValue to NilInt.
-func (s *ValidationErrorErrorsItemValue) SetNilInt(v NilInt) {
-	s.Type = NilIntValidationErrorErrorsItemValue
-	s.NilInt = v
+// SetInt sets ValidationErrorErrorsItemValue to int.
+func (s *ValidationErrorErrorsItemValue) SetInt(v int) {
+	s.Type = IntValidationErrorErrorsItemValue
+	s.Int = v
 }
 
-// GetNilInt returns NilInt and true boolean if ValidationErrorErrorsItemValue is NilInt.
-func (s ValidationErrorErrorsItemValue) GetNilInt() (v NilInt, ok bool) {
-	if !s.IsNilInt() {
+// GetInt returns int and true boolean if ValidationErrorErrorsItemValue is int.
+func (s ValidationErrorErrorsItemValue) GetInt() (v int, ok bool) {
+	if !s.IsInt() {
 		return v, false
 	}
-	return s.NilInt, true
+	return s.Int, true
 }
 
-// NewNilIntValidationErrorErrorsItemValue returns new ValidationErrorErrorsItemValue from NilInt.
-func NewNilIntValidationErrorErrorsItemValue(v NilInt) ValidationErrorErrorsItemValue {
+// NewIntValidationErrorErrorsItemValue returns new ValidationErrorErrorsItemValue from int.
+func NewIntValidationErrorErrorsItemValue(v int) ValidationErrorErrorsItemValue {
 	var s ValidationErrorErrorsItemValue
-	s.SetNilInt(v)
+	s.SetInt(v)
 	return s
 }
 
diff --git a/examples/ex_github/oas_validators_gen.go b/examples/ex_github/oas_validators_gen.go
index dbf103a2..9e268be3 100644
--- a/examples/ex_github/oas_validators_gen.go
+++ b/examples/ex_github/oas_validators_gen.go
@@ -17774,6 +17774,81 @@ func (s UsersSetPrimaryEmailVisibilityForAuthenticatedReqVisibility) Validate()
 		return errors.Errorf("invalid value: %v", s)
 	}
 }
+func (s ValidationError) Validate() error {
+	var failures []validate.FieldError
+	if err := func() error {
+		var failures []validate.FieldError
+		for i, elem := range s.Errors {
+			if err := func() error {
+				if err := elem.Validate(); err != nil {
+					return err
+				}
+				return nil
+			}(); err != nil {
+				failures = append(failures, validate.FieldError{
+					Name:  fmt.Sprintf("[%d]", i),
+					Error: err,
+				})
+			}
+		}
+		if len(failures) > 0 {
+			return &validate.Error{Fields: failures}
+		}
+		return nil
+	}(); err != nil {
+		failures = append(failures, validate.FieldError{
+			Name:  "errors",
+			Error: err,
+		})
+	}
+	if len(failures) > 0 {
+		return &validate.Error{Fields: failures}
+	}
+	return nil
+}
+func (s ValidationErrorErrorsItem) Validate() error {
+	var failures []validate.FieldError
+	if err := func() error {
+		if s.Value.Set {
+			if err := func() error {
+				if err := s.Value.Value.Validate(); err != nil {
+					return err
+				}
+				return nil
+			}(); err != nil {
+				return err
+			}
+		}
+		return nil
+	}(); err != nil {
+		failures = append(failures, validate.FieldError{
+			Name:  "value",
+			Error: err,
+		})
+	}
+	if len(failures) > 0 {
+		return &validate.Error{Fields: failures}
+	}
+	return nil
+}
+func (s ValidationErrorErrorsItemValue) Validate() error {
+	switch s.Type {
+	case NullValidationErrorErrorsItemValue:
+		return nil // no validation needed
+	case StringValidationErrorErrorsItemValue:
+		return nil // no validation needed
+	case IntValidationErrorErrorsItemValue:
+		return nil // no validation needed
+	case StringArrayValidationErrorErrorsItemValue:
+		if s.StringArray == nil {
+			return errors.New("nil is invalid value")
+		}
+		return nil
+	default:
+		return errors.Errorf("invalid type %q", s.Type)
+	}
+}
+
 func (s ViewTraffic) Validate() error {
 	var failures []validate.FieldError
 	if err := func() error {
diff --git a/examples/generate.go b/examples/generate.go
index 5d33f3a1..74401314 100644
--- a/examples/generate.go
+++ b/examples/generate.go
@@ -29,7 +29,7 @@ import (
 //go:generate go run github.com/ogen-go/ogen/cmd/ogen -v --target ex_manga --clean --debug.ignoreNotImplemented "unsupported content types" --generate-tests  ../_testdata/positive/manga.json
 //go:generate go run github.com/ogen-go/ogen/cmd/ogen -v --target ex_petstore_expanded --clean --debug.noerr --generate-tests  ../_testdata/positive/petstore-expanded.yml
 //go:generate go run github.com/ogen-go/ogen/cmd/ogen -v --target ex_k8s --clean --debug.noerr --generate-tests  ../_testdata/positive/k8s.json
-//go:generate go run github.com/ogen-go/ogen/cmd/ogen -v --target ex_github --clean --infer-types --debug.noerr --generate-tests --skip-tests=ValidationErrorErrorsItem* ../_testdata/positive/api.github.com.json
+//go:generate go run github.com/ogen-go/ogen/cmd/ogen -v --target ex_github --clean --infer-types --debug.noerr --generate-tests ../_testdata/positive/api.github.com.json
 //go:generate go run github.com/ogen-go/ogen/cmd/ogen -v --target ex_telegram --clean --debug.noerr --generate-tests  ../_testdata/positive/telegram_bot_api.json
 //go:generate go run github.com/ogen-go/ogen/cmd/ogen -v --target ex_tinkoff --clean --debug.ignoreNotImplemented "http security" --generate-tests  ../_testdata/positive/tinkoff.json
 //go:generate go run github.com/ogen-go/ogen/cmd/ogen -v --target ex_2ch --clean --debug.ignoreNotImplemented "unsupported content types, complex form schema" --generate-tests  ../_testdata/positive/2ch.yml
diff --git a/gen/schema_gen.go b/gen/schema_gen.go
index fa8e2aba..01b96098 100644
--- a/gen/schema_gen.go
+++ b/gen/schema_gen.go
@@ -40,33 +40,33 @@ func variantFieldName(t *ir.Type) string {
 	return capitalize.Capitalize(t.NamePostfix())
 }
 
-func (g *schemaGen) generate(name string, schema *jsonschema.Schema, optional bool) (ret *ir.Type, err error) {
-	if schema == nil {
-		return nil, &ErrNotImplemented{Name: "empty schema"}
+func (g *schemaGen) generate(name string, schema *jsonschema.Schema, optional bool) (*ir.Type, error) {
+	t, err := g.generate2(name, schema)
+	if err != nil {
+		return nil, err
 	}
 
-	defer func() {
-		if err != nil {
-			return
-		}
+	nullable := schema != nil && schema.Nullable
+	t, err = boxType(t, ir.GenericVariant{
+		Optional: optional,
+		Nullable: nullable,
+	})
+	if err != nil {
+		return nil, err
+	}
 
-		nullable := schema != nil && schema.Nullable
-		boxedT, boxErr := boxType(ret, ir.GenericVariant{
-			Optional: optional,
-			Nullable: nullable,
-		})
-		if boxErr != nil {
-			err = boxErr
-			ret = nil
-			return
-		}
+	if t.IsGeneric() {
+		g.side = append(g.side, t)
+	}
 
-		if boxedT.IsGeneric() {
-			g.side = append(g.side, boxedT)
-		}
+	// TODO: update refcache to point to new boxed type?
+	return t, nil
+}
 
-		ret = boxedT
-	}()
+func (g *schemaGen) generate2(name string, schema *jsonschema.Schema) (ret *ir.Type, err error) {
+	if schema == nil {
+		return nil, &ErrNotImplemented{Name: "empty schema"}
+	}
 
 	if ref := schema.Ref; ref != "" {
 		if t, ok := g.lookupRef(ref); ok {
diff --git a/gen/schema_gen_sum.go b/gen/schema_gen_sum.go
index ac1f9b82..704ba6e7 100644
--- a/gen/schema_gen_sum.go
+++ b/gen/schema_gen_sum.go
@@ -62,13 +62,25 @@ func (g *schemaGen) collectSumVariants(
 ) (sum []*ir.Type, _ error) {
 	// TODO(tdakkota): convert oneOf+null into generic
 
+	for _, s := range schemas {
+		if s != nil && s.Nullable {
+			nullT := ir.Primitive(ir.Null, nil)
+			nullT.Name = "Null"
+			sum = append(sum, nullT)
+			break
+		}
+	}
+
 	names := map[string]struct{}{}
 	for i, s := range schemas {
 		if ref := s.Ref; ref != "" && ref == parent.Ref {
 			return nil, errors.Errorf("reference %q [%d] leads to infinite recursion", ref, i)
 		}
 
-		t, err := g.generate(fmt.Sprintf("%s%d", name, i), s, false)
+		// generate without boxing because:
+		// 1) sum variant cannot be optional
+		// 2) if sum variant is nullable - null type already added into sum
+		t, err := g.generate2(fmt.Sprintf("%s%d", name, i), s)
 		if err != nil {
 			return nil, errors.Wrapf(err, "oneOf[%d]", i)
 		}
diff --git a/internal/sample_api/oas_client_gen.go b/internal/sample_api/oas_client_gen.go
index c9111ce9..2e8ae0cb 100644
--- a/internal/sample_api/oas_client_gen.go
+++ b/internal/sample_api/oas_client_gen.go
@@ -1971,6 +1971,48 @@ func (c *Client) TestMultipartUpload(ctx context.Context, request TestMultipartU
 	return result, nil
 }
 
+// TestNullableOneofs invokes testNullableOneofs operation.
+//
+// GET /testNullableOneofs
+func (c *Client) TestNullableOneofs(ctx context.Context) (res TestNullableOneofsRes, err error) {
+	startTime := time.Now()
+	otelAttrs := []attribute.KeyValue{
+		otelogen.OperationID("testNullableOneofs"),
+	}
+	ctx, span := c.cfg.Tracer.Start(ctx, "TestNullableOneofs",
+		trace.WithAttributes(otelAttrs...),
+		trace.WithSpanKind(trace.SpanKindClient),
+	)
+	defer func() {
+		if err != nil {
+			span.RecordError(err)
+			c.errors.Add(ctx, 1, otelAttrs...)
+		} else {
+			elapsedDuration := time.Since(startTime)
+			c.duration.Record(ctx, elapsedDuration.Microseconds(), otelAttrs...)
+		}
+		span.End()
+	}()
+	c.requests.Add(ctx, 1, otelAttrs...)
+	u := uri.Clone(c.serverURL)
+	u.Path += "/testNullableOneofs"
+
+	r := ht.NewRequest(ctx, "GET", u, nil)
+
+	resp, err := c.cfg.Client.Do(r)
+	if err != nil {
+		return res, errors.Wrap(err, "do request")
+	}
+	defer resp.Body.Close()
+
+	result, err := decodeTestNullableOneofsResponse(resp, span)
+	if err != nil {
+		return res, errors.Wrap(err, "decode response")
+	}
+
+	return result, nil
+}
+
 // TestObjectQueryParameter invokes testObjectQueryParameter operation.
 //
 // GET /testObjectQueryParameter
diff --git a/internal/sample_api/oas_faker_gen.go b/internal/sample_api/oas_faker_gen.go
index c844d96d..8cfc8574 100644
--- a/internal/sample_api/oas_faker_gen.go
+++ b/internal/sample_api/oas_faker_gen.go
@@ -555,6 +555,16 @@ func (s *NullableEnumsOnlyNullable) SetFake() {
 	*s = NullableEnumsOnlyNullableAsc
 }
 
+// SetFake set fake values.
+func (s *OneOfBooleanSumNullables) SetFake() {
+	var elem bool
+
+	{
+		elem = true
+	}
+	s.SetBool(elem)
+}
+
 // SetFake set fake values.
 func (s *OneOfBugs) SetFake() {
 	{
@@ -621,6 +631,16 @@ func (s *OneOfMappingReferenceB) SetFake() {
 func (s *OneOfMappingReferenceBData) SetFake() {
 }
 
+// SetFake set fake values.
+func (s *OneOfNullables) SetFake() {
+	var elem struct{}
+
+	{
+		elem = struct{}{}
+	}
+	s.SetNull(elem)
+}
+
 // SetFake set fake values.
 func (s *OneOfUUIDAndIntEnum) SetFake() {
 	var elem uuid.UUID
@@ -636,6 +656,16 @@ func (s *OneOfUUIDAndIntEnum1) SetFake() {
 	*s = OneOfUUIDAndIntEnum10
 }
 
+// SetFake set fake values.
+func (s *OneOfWithNullable) SetFake() {
+	var elem struct{}
+
+	{
+		elem = struct{}{}
+	}
+	s.SetNull(elem)
+}
+
 // SetFake set fake values.
 func (s *OnePropertyObject) SetFake() {
 	{
@@ -1431,6 +1461,24 @@ func (s *TestMultipartUploadOK) SetFake() {
 	}
 }
 
+// SetFake set fake values.
+func (s *TestNullableOneofsApplicationJSONCreated) SetFake() {
+	var unwrapped OneOfWithNullable
+	{
+		unwrapped.SetFake()
+	}
+	*s = TestNullableOneofsApplicationJSONCreated(unwrapped)
+}
+
+// SetFake set fake values.
+func (s *TestNullableOneofsApplicationJSONOK) SetFake() {
+	var unwrapped OneOfWithNullable
+	{
+		unwrapped.SetFake()
+	}
+	*s = TestNullableOneofsApplicationJSONOK(unwrapped)
+}
+
 // SetFake set fake values.
 func (s *TestObjectQueryParameterOK) SetFake() {
 	{
diff --git a/internal/sample_api/oas_handlers_gen.go b/internal/sample_api/oas_handlers_gen.go
index 1df93530..96b0190e 100644
--- a/internal/sample_api/oas_handlers_gen.go
+++ b/internal/sample_api/oas_handlers_gen.go
@@ -1309,6 +1309,42 @@ func (s *Server) handleTestMultipartUploadRequest(args [0]string, w http.Respons
 	s.duration.Record(ctx, elapsedDuration.Microseconds(), otelAttrs...)
 }
 
+// HandleTestNullableOneofsRequest handles testNullableOneofs operation.
+//
+// GET /testNullableOneofs
+func (s *Server) handleTestNullableOneofsRequest(args [0]string, w http.ResponseWriter, r *http.Request) {
+	startTime := time.Now()
+	otelAttrs := []attribute.KeyValue{
+		otelogen.OperationID("testNullableOneofs"),
+	}
+	ctx, span := s.cfg.Tracer.Start(r.Context(), "TestNullableOneofs",
+		trace.WithAttributes(otelAttrs...),
+		trace.WithSpanKind(trace.SpanKindServer),
+	)
+	s.requests.Add(ctx, 1, otelAttrs...)
+	defer span.End()
+
+	var err error
+
+	response, err := s.h.TestNullableOneofs(ctx)
+	if err != nil {
+		span.RecordError(err)
+		span.SetStatus(codes.Error, "Internal")
+		s.errors.Add(ctx, 1, otelAttrs...)
+		s.cfg.ErrorHandler(ctx, w, r, err)
+		return
+	}
+
+	if err := encodeTestNullableOneofsResponse(response, w, span); err != nil {
+		span.RecordError(err)
+		span.SetStatus(codes.Error, "Response")
+		s.errors.Add(ctx, 1, otelAttrs...)
+		return
+	}
+	elapsedDuration := time.Since(startTime)
+	s.duration.Record(ctx, elapsedDuration.Microseconds(), otelAttrs...)
+}
+
 // HandleTestObjectQueryParameterRequest handles testObjectQueryParameter operation.
 //
 // GET /testObjectQueryParameter
diff --git a/internal/sample_api/oas_interfaces_gen.go b/internal/sample_api/oas_interfaces_gen.go
index 9e360c8f..741013f9 100644
--- a/internal/sample_api/oas_interfaces_gen.go
+++ b/internal/sample_api/oas_interfaces_gen.go
@@ -24,3 +24,7 @@ type PetGetRes interface {
 type PetUploadAvatarByIDRes interface {
 	petUploadAvatarByIDRes()
 }
+
+type TestNullableOneofsRes interface {
+	testNullableOneofsRes()
+}
diff --git a/internal/sample_api/oas_json_gen.go b/internal/sample_api/oas_json_gen.go
index 9e9031c9..4e9d4444 100644
--- a/internal/sample_api/oas_json_gen.go
+++ b/internal/sample_api/oas_json_gen.go
@@ -3432,6 +3432,54 @@ func (s *NullableEnumsOnlyNullable) UnmarshalJSON(data []byte) error {
 	return s.Decode(d)
 }
 
+// Encode encodes OneOfBooleanSumNullables as json.
+func (s OneOfBooleanSumNullables) Encode(e *jx.Encoder) {
+	switch s.Type {
+	case BoolOneOfBooleanSumNullables:
+		e.Bool(s.Bool)
+	case OneOfNullablesOneOfBooleanSumNullables:
+		s.OneOfNullables.Encode(e)
+	}
+}
+
+// Decode decodes OneOfBooleanSumNullables from json.
+func (s *OneOfBooleanSumNullables) Decode(d *jx.Decoder) error {
+	if s == nil {
+		return errors.New("invalid: unable to decode OneOfBooleanSumNullables to nil")
+	}
+	// Sum type type_discriminator.
+	switch t := d.Next(); t {
+	case jx.Bool:
+		v, err := d.Bool()
+		s.Bool = bool(v)
+		if err != nil {
+			return err
+		}
+		s.Type = BoolOneOfBooleanSumNullables
+	case jx.Array, jx.Null, jx.Number, jx.String:
+		if err := s.OneOfNullables.Decode(d); err != nil {
+			return err
+		}
+		s.Type = OneOfNullablesOneOfBooleanSumNullables
+	default:
+		return errors.Errorf("unexpected json type %q", t)
+	}
+	return nil
+}
+
+// MarshalJSON implements stdjson.Marshaler.
+func (s OneOfBooleanSumNullables) MarshalJSON() ([]byte, error) {
+	e := jx.Encoder{}
+	s.Encode(&e)
+	return e.Bytes(), nil
+}
+
+// UnmarshalJSON implements stdjson.Unmarshaler.
+func (s *OneOfBooleanSumNullables) UnmarshalJSON(data []byte) error {
+	d := jx.DecodeBytes(data)
+	return s.Decode(d)
+}
+
 // Encode implements json.Marshaler.
 func (s OneOfBugs) Encode(e *jx.Encoder) {
 	e.ObjStart()
@@ -3872,6 +3920,85 @@ func (s *OneOfMappingReferenceBData) UnmarshalJSON(data []byte) error {
 	return s.Decode(d)
 }
 
+// Encode encodes OneOfNullables as json.
+func (s OneOfNullables) Encode(e *jx.Encoder) {
+	switch s.Type {
+	case NullOneOfNullables:
+		_ = s.Null
+		e.Null()
+	case StringOneOfNullables:
+		e.Str(s.String)
+	case IntOneOfNullables:
+		e.Int(s.Int)
+	case StringArrayOneOfNullables:
+		e.ArrStart()
+		for _, elem := range s.StringArray {
+			e.Str(elem)
+		}
+		e.ArrEnd()
+	}
+}
+
+// Decode decodes OneOfNullables from json.
+func (s *OneOfNullables) Decode(d *jx.Decoder) error {
+	if s == nil {
+		return errors.New("invalid: unable to decode OneOfNullables to nil")
+	}
+	// Sum type type_discriminator.
+	switch t := d.Next(); t {
+	case jx.Null:
+		if err := d.Null(); err != nil {
+			return err
+		}
+		s.Type = NullOneOfNullables
+	case jx.String:
+		v, err := d.Str()
+		s.String = string(v)
+		if err != nil {
+			return err
+		}
+		s.Type = StringOneOfNullables
+	case jx.Number:
+		v, err := d.Int()
+		s.Int = int(v)
+		if err != nil {
+			return err
+		}
+		s.Type = IntOneOfNullables
+	case jx.Array:
+		s.StringArray = make([]string, 0)
+		if err := d.Arr(func(d *jx.Decoder) error {
+			var elem string
+			v, err := d.Str()
+			elem = string(v)
+			if err != nil {
+				return err
+			}
+			s.StringArray = append(s.StringArray, elem)
+			return nil
+		}); err != nil {
+			return err
+		}
+		s.Type = StringArrayOneOfNullables
+	default:
+		return errors.Errorf("unexpected json type %q", t)
+	}
+	return nil
+}
+
+// MarshalJSON implements stdjson.Marshaler.
+func (s OneOfNullables) MarshalJSON() ([]byte, error) {
+	e := jx.Encoder{}
+	s.Encode(&e)
+	return e.Bytes(), nil
+}
+
+// UnmarshalJSON implements stdjson.Unmarshaler.
+func (s *OneOfNullables) UnmarshalJSON(data []byte) error {
+	d := jx.DecodeBytes(data)
+	return s.Decode(d)
+}
+
 // Encode encodes OneOfUUIDAndIntEnum as json.
 func (s OneOfUUIDAndIntEnum) Encode(e *jx.Encoder) {
 	switch s.Type {
@@ -3952,6 +4079,85 @@ func (s *OneOfUUIDAndIntEnum1) UnmarshalJSON(data []byte) error {
 	return s.Decode(d)
 }
 
+// Encode encodes OneOfWithNullable as json.
+func (s OneOfWithNullable) Encode(e *jx.Encoder) {
+	switch s.Type {
+	case NullOneOfWithNullable:
+		_ = s.Null
+		e.Null()
+	case StringOneOfWithNullable:
+		e.Str(s.String)
+	case IntOneOfWithNullable:
+		e.Int(s.Int)
+	case StringArrayOneOfWithNullable:
+		e.ArrStart()
+		for _, elem := range s.StringArray {
+			e.Str(elem)
+		}
+		e.ArrEnd()
+	}
+}
+
+// Decode decodes OneOfWithNullable from json.
+func (s *OneOfWithNullable) Decode(d *jx.Decoder) error {
+	if s == nil {
+		return errors.New("invalid: unable to decode OneOfWithNullable to nil")
+	}
+	// Sum type type_discriminator.
+	switch t := d.Next(); t {
+	case jx.Null:
+		if err := d.Null(); err != nil {
+			return err
+		}
+		s.Type = NullOneOfWithNullable
+	case jx.String:
+		v, err := d.Str()
+		s.String = string(v)
+		if err != nil {
+			return err
+		}
+		s.Type = StringOneOfWithNullable
+	case jx.Number:
+		v, err := d.Int()
+		s.Int = int(v)
+		if err != nil {
+			return err
+		}
+		s.Type = IntOneOfWithNullable
+	case jx.Array:
+		s.StringArray = make([]string, 0)
+		if err := d.Arr(func(d *jx.Decoder) error {
+			var elem string
+			v, err := d.Str()
+			elem = string(v)
+			if err != nil {
+				return err
+			}
+			s.StringArray = append(s.StringArray, elem)
+			return nil
+		}); err != nil {
+			return err
+		}
+		s.Type = StringArrayOneOfWithNullable
+	default:
+		return errors.Errorf("unexpected json type %q", t)
+	}
+	return nil
+}
+
+// MarshalJSON implements stdjson.Marshaler.
+func (s OneOfWithNullable) MarshalJSON() ([]byte, error) {
+	e := jx.Encoder{}
+	s.Encode(&e)
+	return e.Bytes(), nil
+}
+
+// UnmarshalJSON implements stdjson.Unmarshaler.
+func (s *OneOfWithNullable) UnmarshalJSON(data []byte) error {
+	d := jx.DecodeBytes(data)
+	return s.Decode(d)
+}
+
 // Encode implements json.Marshaler.
 func (s OnePropertyObject) Encode(e *jx.Encoder) {
 	e.ObjStart()
@@ -7674,6 +7880,82 @@ func (s *TestMultipartUploadOK) UnmarshalJSON(data []byte) error {
 	return s.Decode(d)
 }
 
+// Encode encodes TestNullableOneofsApplicationJSONCreated as json.
+func (s TestNullableOneofsApplicationJSONCreated) Encode(e *jx.Encoder) {
+	unwrapped := OneOfWithNullable(s)
+
+	unwrapped.Encode(e)
+}
+
+// Decode decodes TestNullableOneofsApplicationJSONCreated from json.
+func (s *TestNullableOneofsApplicationJSONCreated) Decode(d *jx.Decoder) error {
+	if s == nil {
+		return errors.New("invalid: unable to decode TestNullableOneofsApplicationJSONCreated to nil")
+	}
+	var unwrapped OneOfWithNullable
+	if err := func() error {
+		if err := unwrapped.Decode(d); err != nil {
+			return err
+		}
+		return nil
+	}(); err != nil {
+		return errors.Wrap(err, "alias")
+	}
+	*s = TestNullableOneofsApplicationJSONCreated(unwrapped)
+	return nil
+}
+
+// MarshalJSON implements stdjson.Marshaler.
+func (s TestNullableOneofsApplicationJSONCreated) MarshalJSON() ([]byte, error) {
+	e := jx.Encoder{}
+	s.Encode(&e)
+	return e.Bytes(), nil
+}
+
+// UnmarshalJSON implements stdjson.Unmarshaler.
+func (s *TestNullableOneofsApplicationJSONCreated) UnmarshalJSON(data []byte) error {
+	d := jx.DecodeBytes(data)
+	return s.Decode(d)
+}
+
+// Encode encodes TestNullableOneofsApplicationJSONOK as json.
+func (s TestNullableOneofsApplicationJSONOK) Encode(e *jx.Encoder) {
+	unwrapped := OneOfWithNullable(s)
+
+	unwrapped.Encode(e)
+}
+
+// Decode decodes TestNullableOneofsApplicationJSONOK from json.
+func (s *TestNullableOneofsApplicationJSONOK) Decode(d *jx.Decoder) error {
+	if s == nil {
+		return errors.New("invalid: unable to decode TestNullableOneofsApplicationJSONOK to nil")
+	}
+	var unwrapped OneOfWithNullable
+	if err := func() error {
+		if err := unwrapped.Decode(d); err != nil {
+			return err
+		}
+		return nil
+	}(); err != nil {
+		return errors.Wrap(err, "alias")
+	}
+	*s = TestNullableOneofsApplicationJSONOK(unwrapped)
+	return nil
+}
+
+// MarshalJSON implements stdjson.Marshaler.
+func (s TestNullableOneofsApplicationJSONOK) MarshalJSON() ([]byte, error) {
+	e := jx.Encoder{}
+	s.Encode(&e)
+	return e.Bytes(), nil
+}
+
+// UnmarshalJSON implements stdjson.Unmarshaler.
+func (s *TestNullableOneofsApplicationJSONOK) UnmarshalJSON(data []byte) error {
+	d := jx.DecodeBytes(data)
+	return s.Decode(d)
+}
+
 // Encode implements json.Marshaler.
 func (s TestObjectQueryParameterOK) Encode(e *jx.Encoder) {
 	e.ObjStart()
diff --git a/internal/sample_api/oas_response_decoders_gen.go b/internal/sample_api/oas_response_decoders_gen.go
index ce5c0949..5537fa56 100644
--- a/internal/sample_api/oas_response_decoders_gen.go
+++ b/internal/sample_api/oas_response_decoders_gen.go
@@ -1068,6 +1068,105 @@ func decodeTestMultipartUploadResponse(resp *http.Response, span trace.Span) (re
 		return res, validate.UnexpectedStatusCode(resp.StatusCode)
 	}
 }
+func decodeTestNullableOneofsResponse(resp *http.Response, span trace.Span) (res TestNullableOneofsRes, err error) {
+	switch resp.StatusCode {
+	case 200:
+		match := func(pattern, value string) bool {
+			ok, _ := path.Match(pattern, value)
+			return ok
+		}
+		_ = match
+		ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
+		if err != nil {
+			return res, errors.Wrap(err, "parse media type")
+		}
+		switch {
+		case ct == "application/json":
+			buf := new(bytes.Buffer)
+			if _, err := io.Copy(buf, resp.Body); err != nil {
+				return res, err
+			}
+
+			d := jx.DecodeBytes(buf.Bytes())
+			var response TestNullableOneofsApplicationJSONOK
+			if err := func() error {
+				if err := response.Decode(d); err != nil {
+					return err
+				}
+				return nil
+			}(); err != nil {
+				return res, err
+			}
+			return &response, nil
+		default:
+			return res, validate.InvalidContentType(ct)
+		}
+	case 201:
+		match := func(pattern, value string) bool {
+			ok, _ := path.Match(pattern, value)
+			return ok
+		}
+		_ = match
+		ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
+		if err != nil {
+			return res, errors.Wrap(err, "parse media type")
+		}
+		switch {
+		case ct == "application/json":
+			buf := new(bytes.Buffer)
+			if _, err := io.Copy(buf, resp.Body); err != nil {
+				return res, err
+			}
+
+			d := jx.DecodeBytes(buf.Bytes())
+			var response TestNullableOneofsApplicationJSONCreated
+			if err := func() error {
+				if err := response.Decode(d); err != nil {
+					return err
+				}
+				return nil
+			}(); err != nil {
+				return res, err
+			}
+			return &response, nil
+		default:
+			return res, validate.InvalidContentType(ct)
+		}
+	case 202:
+		match := func(pattern, value string) bool {
+			ok, _ := path.Match(pattern, value)
+			return ok
+		}
+		_ = match
+		ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
+		if err != nil {
+			return res, errors.Wrap(err, "parse media type")
+		}
+		switch {
+		case ct == "application/json":
+			buf := new(bytes.Buffer)
+			if _, err := io.Copy(buf, resp.Body); err != nil {
+				return res, err
+			}
+
+			d := jx.DecodeBytes(buf.Bytes())
+			var response OneOfBooleanSumNullables
+			if err := func() error {
+				if err := response.Decode(d); err != nil {
+					return err
+				}
+				return nil
+			}(); err != nil {
+				return res, err
+			}
+			return &response, nil
+		default:
+			return res, validate.InvalidContentType(ct)
+		}
+	default:
+		return res, validate.UnexpectedStatusCode(resp.StatusCode)
+	}
+}
 func decodeTestObjectQueryParameterResponse(resp *http.Response, span trace.Span) (res TestObjectQueryParameterOK, err error) {
 	switch resp.StatusCode {
 	case 200:
diff --git a/internal/sample_api/oas_response_encoders_gen.go b/internal/sample_api/oas_response_encoders_gen.go
index 6db735c8..31b17ad2 100644
--- a/internal/sample_api/oas_response_encoders_gen.go
+++ b/internal/sample_api/oas_response_encoders_gen.go
@@ -509,6 +509,48 @@ func encodeTestMultipartUploadResponse(response TestMultipartUploadOK, w http.Re
 	return nil
 
 }
+func encodeTestNullableOneofsResponse(response TestNullableOneofsRes, w http.ResponseWriter, span trace.Span) error {
+	switch response := response.(type) {
+	case *TestNullableOneofsApplicationJSONOK:
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(200)
+		span.SetStatus(codes.Ok, http.StatusText(200))
+		e := jx.GetEncoder()
+
+		response.Encode(e)
+		if _, err := e.WriteTo(w); err != nil {
+			return errors.Wrap(err, "write")
+		}
+		return nil
+
+	case *TestNullableOneofsApplicationJSONCreated:
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(201)
+		span.SetStatus(codes.Ok, http.StatusText(201))
+		e := jx.GetEncoder()
+
+		response.Encode(e)
+		if _, err := e.WriteTo(w); err != nil {
+			return errors.Wrap(err, "write")
+		}
+		return nil
+
+	case *OneOfBooleanSumNullables:
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(202)
+		span.SetStatus(codes.Ok, http.StatusText(202))
+		e := jx.GetEncoder()
+
+		response.Encode(e)
+		if _, err := e.WriteTo(w); err != nil {
+			return errors.Wrap(err, "write")
+		}
+		return nil
+
+	default:
+		return errors.Errorf("/testNullableOneofs"+`: unexpected response type: %T`, response)
+	}
+}
 func encodeTestObjectQueryParameterResponse(response TestObjectQueryParameterOK, w http.ResponseWriter, span trace.Span) error {
 	w.Header().Set("Content-Type", "application/json")
 	w.WriteHeader(200)
diff --git a/internal/sample_api/oas_router_gen.go b/internal/sample_api/oas_router_gen.go
index 09f2be00..f1ac0b4a 100644
--- a/internal/sample_api/oas_router_gen.go
+++ b/internal/sample_api/oas_router_gen.go
@@ -458,6 +458,19 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 						// Leaf: TestContentParameter
 						s.handleTestContentParameterRequest([0]string{}, w, r)
 
+						return
+					}
+				case 'N': // Prefix: "NullableOneofs"
+					if l := len("NullableOneofs"); len(elem) >= l && elem[0:l] == "NullableOneofs" {
+						elem = elem[l:]
+					} else {
+						break
+					}
+
+					if len(elem) == 0 {
+						// Leaf: TestNullableOneofs
+						s.handleTestNullableOneofsRequest([0]string{}, w, r)
+
 						return
 					}
 				case 'O': // Prefix: "ObjectQueryParameter"
@@ -1171,6 +1184,20 @@ func (s *Server) FindRoute(method, path string) (r Route, _ bool) {
 						r.count = 0
 						return r, true
 					}
+				case 'N': // Prefix: "NullableOneofs"
+					if l := len("NullableOneofs"); len(elem) >= l && elem[0:l] == "NullableOneofs" {
+						elem = elem[l:]
+					} else {
+						break
+					}
+
+					if len(elem) == 0 {
+						// Leaf: TestNullableOneofs
+						r.name = "TestNullableOneofs"
+						r.args = args
+						r.count = 0
+						return r, true
+					}
 				case 'O': // Prefix: "ObjectQueryParameter"
 					if l := len("ObjectQueryParameter"); len(elem) >= l && elem[0:l] == "ObjectQueryParameter" {
 						elem = elem[l:]
diff --git a/internal/sample_api/oas_schemas_gen.go b/internal/sample_api/oas_schemas_gen.go
index a264bb77..543c761c 100644
--- a/internal/sample_api/oas_schemas_gen.go
+++ b/internal/sample_api/oas_schemas_gen.go
@@ -754,6 +754,75 @@ const (
 	NullableEnumsOnlyNullableDesc NullableEnumsOnlyNullable = "desc"
 )
 
+// Ref: #/components/schemas/OneOfBooleanSumNullables
+// OneOfBooleanSumNullables represents sum type.
+type OneOfBooleanSumNullables struct {
+	Type           OneOfBooleanSumNullablesType // switch on this field
+	Bool           bool
+	OneOfNullables OneOfNullables
+}
+
+// OneOfBooleanSumNullablesType is oneOf type of OneOfBooleanSumNullables.
+type OneOfBooleanSumNullablesType string
+
+// Possible values for OneOfBooleanSumNullablesType.
+const (
+	BoolOneOfBooleanSumNullables           OneOfBooleanSumNullablesType = "bool"
+	OneOfNullablesOneOfBooleanSumNullables OneOfBooleanSumNullablesType = "OneOfNullables"
+)
+
+// IsBool reports whether OneOfBooleanSumNullables is bool.
+func (s OneOfBooleanSumNullables) IsBool() bool { return s.Type == BoolOneOfBooleanSumNullables }
+
+// IsOneOfNullables reports whether OneOfBooleanSumNullables is OneOfNullables.
+func (s OneOfBooleanSumNullables) IsOneOfNullables() bool {
+	return s.Type == OneOfNullablesOneOfBooleanSumNullables
+}
+
+// SetBool sets OneOfBooleanSumNullables to bool.
+func (s *OneOfBooleanSumNullables) SetBool(v bool) {
+	s.Type = BoolOneOfBooleanSumNullables
+	s.Bool = v
+}
+
+// GetBool returns bool and true boolean if OneOfBooleanSumNullables is bool.
+func (s OneOfBooleanSumNullables) GetBool() (v bool, ok bool) {
+	if !s.IsBool() {
+		return v, false
+	}
+	return s.Bool, true
+}
+
+// NewBoolOneOfBooleanSumNullables returns new OneOfBooleanSumNullables from bool.
+func NewBoolOneOfBooleanSumNullables(v bool) OneOfBooleanSumNullables {
+	var s OneOfBooleanSumNullables
+	s.SetBool(v)
+	return s
+}
+
+// SetOneOfNullables sets OneOfBooleanSumNullables to OneOfNullables.
+func (s *OneOfBooleanSumNullables) SetOneOfNullables(v OneOfNullables) {
+	s.Type = OneOfNullablesOneOfBooleanSumNullables
+	s.OneOfNullables = v
+}
+
+// GetOneOfNullables returns OneOfNullables and true boolean if OneOfBooleanSumNullables is OneOfNullables.
+func (s OneOfBooleanSumNullables) GetOneOfNullables() (v OneOfNullables, ok bool) {
+	if !s.IsOneOfNullables() {
+		return v, false
+	}
+	return s.OneOfNullables, true
+}
+
+// NewOneOfNullablesOneOfBooleanSumNullables returns new OneOfBooleanSumNullables from OneOfNullables.
+func NewOneOfNullablesOneOfBooleanSumNullables(v OneOfNullables) OneOfBooleanSumNullables {
+	var s OneOfBooleanSumNullables
+	s.SetOneOfNullables(v)
+	return s
+}
+
+func (*OneOfBooleanSumNullables) testNullableOneofsRes() {}
+
 // Ref: #/components/schemas/OneOfBugs
 type OneOfBugs struct {
 	Issue143                        Issue143                    "json:\"issue143\""
@@ -845,6 +914,123 @@ type OneOfMappingReferenceB struct {
 
 type OneOfMappingReferenceBData struct{}
 
+// Ref: #/components/schemas/OneOfNullables
+// OneOfNullables represents sum type.
+type OneOfNullables struct {
+	Type        OneOfNullablesType // switch on this field
+	Null        struct{}
+	String      string
+	Int         int
+	StringArray []string
+}
+
+// OneOfNullablesType is oneOf type of OneOfNullables.
+type OneOfNullablesType string
+
+// Possible values for OneOfNullablesType.
+const (
+	NullOneOfNullables        OneOfNullablesType = "struct{}"
+	StringOneOfNullables      OneOfNullablesType = "string"
+	IntOneOfNullables         OneOfNullablesType = "int"
+	StringArrayOneOfNullables OneOfNullablesType = "[]string"
+)
+
+// IsNull reports whether OneOfNullables is struct{}.
+func (s OneOfNullables) IsNull() bool { return s.Type == NullOneOfNullables }
+
+// IsString reports whether OneOfNullables is string.
+func (s OneOfNullables) IsString() bool { return s.Type == StringOneOfNullables }
+
+// IsInt reports whether OneOfNullables is int.
+func (s OneOfNullables) IsInt() bool { return s.Type == IntOneOfNullables }
+
+// IsStringArray reports whether OneOfNullables is []string.
+func (s OneOfNullables) IsStringArray() bool { return s.Type == StringArrayOneOfNullables }
+
+// SetNull sets OneOfNullables to struct{}.
+func (s *OneOfNullables) SetNull(v struct{}) {
+	s.Type = NullOneOfNullables
+	s.Null = v
+}
+
+// GetNull returns struct{} and true boolean if OneOfNullables is struct{}.
+func (s OneOfNullables) GetNull() (v struct{}, ok bool) {
+	if !s.IsNull() {
+		return v, false
+	}
+	return s.Null, true
+}
+
+// NewNullOneOfNullables returns new OneOfNullables from struct{}.
+func NewNullOneOfNullables(v struct{}) OneOfNullables {
+	var s OneOfNullables
+	s.SetNull(v)
+	return s
+}
+
+// SetString sets OneOfNullables to string.
+func (s *OneOfNullables) SetString(v string) {
+	s.Type = StringOneOfNullables
+	s.String = v
+}
+
+// GetString returns string and true boolean if OneOfNullables is string.
+func (s OneOfNullables) GetString() (v string, ok bool) {
+	if !s.IsString() {
+		return v, false
+	}
+	return s.String, true
+}
+
+// NewStringOneOfNullables returns new OneOfNullables from string.
+func NewStringOneOfNullables(v string) OneOfNullables {
+	var s OneOfNullables
+	s.SetString(v)
+	return s
+}
+
+// SetInt sets OneOfNullables to int.
+func (s *OneOfNullables) SetInt(v int) {
+	s.Type = IntOneOfNullables
+	s.Int = v
+}
+
+// GetInt returns int and true boolean if OneOfNullables is int.
+func (s OneOfNullables) GetInt() (v int, ok bool) {
+	if !s.IsInt() {
+		return v, false
+	}
+	return s.Int, true
+}
+
+// NewIntOneOfNullables returns new OneOfNullables from int.
+func NewIntOneOfNullables(v int) OneOfNullables {
+	var s OneOfNullables
+	s.SetInt(v)
+	return s
+}
+
+// SetStringArray sets OneOfNullables to []string.
+func (s *OneOfNullables) SetStringArray(v []string) {
+	s.Type = StringArrayOneOfNullables
+	s.StringArray = v
+}
+
+// GetStringArray returns []string and true boolean if OneOfNullables is []string.
+func (s OneOfNullables) GetStringArray() (v []string, ok bool) {
+	if !s.IsStringArray() {
+		return v, false
+	}
+	return s.StringArray, true
+}
+
+// NewStringArrayOneOfNullables returns new OneOfNullables from []string.
+func NewStringArrayOneOfNullables(v []string) OneOfNullables {
+	var s OneOfNullables
+	s.SetStringArray(v)
+	return s
+}
+
 // Ref: #/components/schemas/OneOfUUIDAndIntEnum
 // OneOfUUIDAndIntEnum represents sum type.
 type OneOfUUIDAndIntEnum struct {
@@ -919,6 +1105,123 @@ const (
 	OneOfUUIDAndIntEnum11 OneOfUUIDAndIntEnum1 = 1
 )
 
+// Ref: #/components/schemas/OneOfWithNullable
+// OneOfWithNullable represents sum type.
+type OneOfWithNullable struct {
+	Type        OneOfWithNullableType // switch on this field
+	Null        struct{}
+	String      string
+	Int         int
+	StringArray []string
+}
+
+// OneOfWithNullableType is oneOf type of OneOfWithNullable.
+type OneOfWithNullableType string
+
+// Possible values for OneOfWithNullableType.
+const (
+	NullOneOfWithNullable        OneOfWithNullableType = "struct{}"
+	StringOneOfWithNullable      OneOfWithNullableType = "string"
+	IntOneOfWithNullable         OneOfWithNullableType = "int"
+	StringArrayOneOfWithNullable OneOfWithNullableType = "[]string"
+)
+
+// IsNull reports whether OneOfWithNullable is struct{}.
+func (s OneOfWithNullable) IsNull() bool { return s.Type == NullOneOfWithNullable }
+
+// IsString reports whether OneOfWithNullable is string.
+func (s OneOfWithNullable) IsString() bool { return s.Type == StringOneOfWithNullable }
+
+// IsInt reports whether OneOfWithNullable is int.
+func (s OneOfWithNullable) IsInt() bool { return s.Type == IntOneOfWithNullable }
+
+// IsStringArray reports whether OneOfWithNullable is []string.
+func (s OneOfWithNullable) IsStringArray() bool { return s.Type == StringArrayOneOfWithNullable }
+
+// SetNull sets OneOfWithNullable to struct{}.
+func (s *OneOfWithNullable) SetNull(v struct{}) {
+	s.Type = NullOneOfWithNullable
+	s.Null = v
+}
+
+// GetNull returns struct{} and true boolean if OneOfWithNullable is struct{}.
+func (s OneOfWithNullable) GetNull() (v struct{}, ok bool) {
+	if !s.IsNull() {
+		return v, false
+	}
+	return s.Null, true
+}
+
+// NewNullOneOfWithNullable returns new OneOfWithNullable from struct{}.
+func NewNullOneOfWithNullable(v struct{}) OneOfWithNullable {
+	var s OneOfWithNullable
+	s.SetNull(v)
+	return s
+}
+
+// SetString sets OneOfWithNullable to string.
+func (s *OneOfWithNullable) SetString(v string) {
+	s.Type = StringOneOfWithNullable
+	s.String = v
+}
+
+// GetString returns string and true boolean if OneOfWithNullable is string.
+func (s OneOfWithNullable) GetString() (v string, ok bool) {
+	if !s.IsString() {
+		return v, false
+	}
+	return s.String, true
+}
+
+// NewStringOneOfWithNullable returns new OneOfWithNullable from string.
+func NewStringOneOfWithNullable(v string) OneOfWithNullable {
+	var s OneOfWithNullable
+	s.SetString(v)
+	return s
+}
+
+// SetInt sets OneOfWithNullable to int.
+func (s *OneOfWithNullable) SetInt(v int) {
+	s.Type = IntOneOfWithNullable
+	s.Int = v
+}
+
+// GetInt returns int and true boolean if OneOfWithNullable is int.
+func (s OneOfWithNullable) GetInt() (v int, ok bool) {
+	if !s.IsInt() {
+		return v, false
+	}
+	return s.Int, true
+}
+
+// NewIntOneOfWithNullable returns new OneOfWithNullable from int.
+func NewIntOneOfWithNullable(v int) OneOfWithNullable {
+	var s OneOfWithNullable
+	s.SetInt(v)
+	return s
+}
+
+// SetStringArray sets OneOfWithNullable to []string.
+func (s *OneOfWithNullable) SetStringArray(v []string) {
+	s.Type = StringArrayOneOfWithNullable
+	s.StringArray = v
+}
+
+// GetStringArray returns []string and true boolean if OneOfWithNullable is []string.
+func (s OneOfWithNullable) GetStringArray() (v []string, ok bool) {
+	if !s.IsStringArray() {
+		return v, false
+	}
+	return s.StringArray, true
+}
+
+// NewStringArrayOneOfWithNullable returns new OneOfWithNullable from []string.
+func NewStringArrayOneOfWithNullable(v []string) OneOfWithNullable {
+	var s OneOfWithNullable
+	s.SetStringArray(v)
+	return s
+}
+
 // Ref: #/components/schemas/OnePropertyObject
 type OnePropertyObject struct {
 	Foo string "json:\"foo\""
@@ -3271,6 +3574,14 @@ type TestMultipartUploadReq struct {
 	Files        []ht.MultipartFile "json:\"files\""
 }
 
+type TestNullableOneofsApplicationJSONCreated OneOfWithNullable
+
+func (*TestNullableOneofsApplicationJSONCreated) testNullableOneofsRes() {}
+
+type TestNullableOneofsApplicationJSONOK OneOfWithNullable
+
+func (*TestNullableOneofsApplicationJSONOK) testNullableOneofsRes() {}
+
 type TestObjectQueryParameterDeepObject struct {
 	Min    int    "json:\"min\""
 	Max    int    "json:\"max\""
diff --git a/internal/sample_api/oas_server_gen.go b/internal/sample_api/oas_server_gen.go
index 150459c8..be5434d6 100644
--- a/internal/sample_api/oas_server_gen.go
+++ b/internal/sample_api/oas_server_gen.go
@@ -158,6 +158,10 @@ type Handler interface {
 	//
 	// POST /testMultipartUpload
 	TestMultipartUpload(ctx context.Context, req TestMultipartUploadReq) (TestMultipartUploadOK, error)
+	// TestNullableOneofs implements testNullableOneofs operation.
+	//
+	// GET /testNullableOneofs
+	TestNullableOneofs(ctx context.Context) (TestNullableOneofsRes, error)
 	// TestObjectQueryParameter implements testObjectQueryParameter operation.
 	//
 	// GET /testObjectQueryParameter
diff --git a/internal/sample_api/oas_unimplemented_gen.go b/internal/sample_api/oas_unimplemented_gen.go
index 9a13f9c5..0b560474 100644
--- a/internal/sample_api/oas_unimplemented_gen.go
+++ b/internal/sample_api/oas_unimplemented_gen.go
@@ -249,6 +249,13 @@ func (UnimplementedHandler) TestMultipartUpload(ctx context.Context, req TestMul
 	return r, ht.ErrNotImplemented
 }
 
+// TestNullableOneofs implements testNullableOneofs operation.
+//
+// GET /testNullableOneofs
+func (UnimplementedHandler) TestNullableOneofs(ctx context.Context) (r TestNullableOneofsRes, _ error) {
+	return r, ht.ErrNotImplemented
+}
+
 // TestObjectQueryParameter implements testObjectQueryParameter operation.
 //
 // GET /testObjectQueryParameter
diff --git a/internal/sample_api/oas_validators_gen.go b/internal/sample_api/oas_validators_gen.go
index d8874ccf..073aa9ba 100644
--- a/internal/sample_api/oas_validators_gen.go
+++ b/internal/sample_api/oas_validators_gen.go
@@ -421,6 +421,20 @@ func (s NullableEnumsOnlyNullable) Validate() error {
 		return errors.Errorf("invalid value: %v", s)
 	}
 }
+func (s OneOfBooleanSumNullables) Validate() error {
+	switch s.Type {
+	case BoolOneOfBooleanSumNullables:
+		return nil // no validation needed
+	case OneOfNullablesOneOfBooleanSumNullables:
+		if err := s.OneOfNullables.Validate(); err != nil {
+			return err
+		}
+		return nil
+	default:
+		return errors.Errorf("invalid type %q", s.Type)
+	}
+}
+
 func (s OneOfBugs) Validate() error {
 	var failures []validate.FieldError
 	if err := func() error {
@@ -446,6 +460,24 @@ func (s OneOfBugs) Validate() error {
 	}
 	return nil
 }
+func (s OneOfNullables) Validate() error {
+	switch s.Type {
+	case NullOneOfNullables:
+		return nil // no validation needed
+	case StringOneOfNullables:
+		return nil // no validation needed
+	case IntOneOfNullables:
+		return nil // no validation needed
+	case StringArrayOneOfNullables:
+		if s.StringArray == nil {
+			return errors.New("nil is invalid value")
+		}
+		return nil
+	default:
+		return errors.Errorf("invalid type %q", s.Type)
+	}
+}
+
 func (s OneOfUUIDAndIntEnum) Validate() error {
 	switch s.Type {
 	case UUIDOneOfUUIDAndIntEnum:
@@ -470,6 +502,23 @@ func (s OneOfUUIDAndIntEnum1) Validate() error {
 		return errors.Errorf("invalid value: %v", s)
 	}
 }
+func (s OneOfWithNullable) Validate() error {
+	switch s.Type {
+	case NullOneOfWithNullable:
+		return nil // no validation needed
+	case StringOneOfWithNullable:
+		return nil // no validation needed
+	case IntOneOfWithNullable:
+		return nil // no validation needed
+	case StringArrayOneOfWithNullable:
+		if s.StringArray == nil {
+			return errors.New("nil is invalid value")
+		}
+		return nil
+	default:
+		return errors.Errorf("invalid type %q", s.Type)
+	}
+}
 
 func (s Pet) Validate() error {
 	var failures []validate.FieldError
@@ -1003,6 +1052,18 @@ func (s TestMultipartUploadReq) Validate() error {
 	}
 	return nil
 }
+func (s TestNullableOneofsApplicationJSONCreated) Validate() error {
+	if err := s.Validate(); err != nil {
+		return err
+	}
+	return nil
+}
+func (s TestNullableOneofsApplicationJSONOK) Validate() error {
+	if err := s.Validate(); err != nil {
+		return err
+	}
+	return nil
+}
 func (s ValidationStringMap) Validate() error {
 	var failures []validate.FieldError
 	for key, elem := range s {


Test patch:
diff --git a/_testdata/positive/sample.json b/_testdata/positive/sample.json
index 2b611c86..4028c82b 100644
--- a/_testdata/positive/sample.json
+++ b/_testdata/positive/sample.json
@@ -985,6 +985,43 @@
           }
         }
       }
+    },
+    "/testNullableOneofs": {
+      "get": {
+        "operationId": "testNullableOneofs",
+        "responses": {
+          "200": {
+            "description": "OneOfWithNullable",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/OneOfWithNullable"
+                }
+              }
+            }
+          },
+          "201": {
+            "description": "OneOfWithNullable",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/OneOfWithNullable"
+                }
+              }
+            }
+          },
+          "202": {
+            "description": "OneOfBooleanSumNullables",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/OneOfBooleanSumNullables"
+                }
+              }
+            }
+          }
+        }
+      }
     }
   },
   "components": {
@@ -1741,6 +1778,52 @@
           }
         }
       },
+      "OneOfWithNullable": {
+        "oneOf": [
+          {
+            "type": "string"
+          },
+          {
+            "type": "integer",
+            "nullable": true
+          },
+          {
+            "type": "array",
+            "items": {
+              "type": "string"
+            }
+          }
+        ]
+      },
+      "OneOfNullables": {
+        "oneOf": [
+          {
+            "type": "string",
+            "nullable": true
+          },
+          {
+            "type": "integer",
+            "nullable": true
+          },
+          {
+            "type": "array",
+            "nullable": true,
+            "items": {
+              "type": "string"
+            }
+          }
+        ]
+      },
+      "OneOfBooleanSumNullables": {
+        "oneOf": [
+          {
+            "type": "boolean"
+          },
+          {
+            "$ref": "#/components/schemas/OneOfNullables"
+          }
+        ]
+      },
       "TestFloatValidation": {
         "type": "object",
         "required": [
diff --git a/examples/ex_github/oas_test_examples_gen_test.go b/examples/ex_github/oas_test_examples_gen_test.go
index 4fb12761..8aa4e907 100644
--- a/examples/ex_github/oas_test_examples_gen_test.go
+++ b/examples/ex_github/oas_test_examples_gen_test.go
@@ -26797,7 +26797,6 @@ func TestValidationError_EncodeDecode(t *testing.T) {
 	require.NoError(t, typ2.Decode(jx.DecodeBytes(data)))
 }
 func TestValidationErrorErrorsItem_EncodeDecode(t *testing.T) {
-	t.Skip("Skipped by regex")
 	var typ ValidationErrorErrorsItem
 	typ.SetFake()
 
@@ -26810,7 +26809,6 @@ func TestValidationErrorErrorsItem_EncodeDecode(t *testing.T) {
 	require.NoError(t, typ2.Decode(jx.DecodeBytes(data)))
 }
 func TestValidationErrorErrorsItemValue_EncodeDecode(t *testing.T) {
-	t.Skip("Skipped by regex")
 	var typ ValidationErrorErrorsItemValue
 	typ.SetFake()
 
diff --git a/internal/sample_api/oas_test_examples_gen_test.go b/internal/sample_api/oas_test_examples_gen_test.go
index 6ab095b7..d35efc4c 100644
--- a/internal/sample_api/oas_test_examples_gen_test.go
+++ b/internal/sample_api/oas_test_examples_gen_test.go
@@ -365,6 +365,18 @@ func TestNullableEnumsOnlyNullable_EncodeDecode(t *testing.T) {
 	var typ2 NullableEnumsOnlyNullable
 	require.NoError(t, typ2.Decode(jx.DecodeBytes(data)))
 }
+func TestOneOfBooleanSumNullables_EncodeDecode(t *testing.T) {
+	var typ OneOfBooleanSumNullables
+	typ.SetFake()
+
+	e := jx.Encoder{}
+	typ.Encode(&e)
+	data := e.Bytes()
+	require.True(t, std.Valid(data), "Encoded: %s", data)
+
+	var typ2 OneOfBooleanSumNullables
+	require.NoError(t, typ2.Decode(jx.DecodeBytes(data)))
+}
 func TestOneOfBugs_EncodeDecode(t *testing.T) {
 	var typ OneOfBugs
 	typ.SetFake()
@@ -425,6 +437,18 @@ func TestOneOfMappingReferenceBData_EncodeDecode(t *testing.T) {
 	var typ2 OneOfMappingReferenceBData
 	require.NoError(t, typ2.Decode(jx.DecodeBytes(data)))
 }
+func TestOneOfNullables_EncodeDecode(t *testing.T) {
+	var typ OneOfNullables
+	typ.SetFake()
+
+	e := jx.Encoder{}
+	typ.Encode(&e)
+	data := e.Bytes()
+	require.True(t, std.Valid(data), "Encoded: %s", data)
+
+	var typ2 OneOfNullables
+	require.NoError(t, typ2.Decode(jx.DecodeBytes(data)))
+}
 func TestOneOfUUIDAndIntEnum_EncodeDecode(t *testing.T) {
 	var typ OneOfUUIDAndIntEnum
 	typ.SetFake()
@@ -449,6 +473,18 @@ func TestOneOfUUIDAndIntEnum1_EncodeDecode(t *testing.T) {
 	var typ2 OneOfUUIDAndIntEnum1
 	require.NoError(t, typ2.Decode(jx.DecodeBytes(data)))
 }
+func TestOneOfWithNullable_EncodeDecode(t *testing.T) {
+	var typ OneOfWithNullable
+	typ.SetFake()
+
+	e := jx.Encoder{}
+	typ.Encode(&e)
+	data := e.Bytes()
+	require.True(t, std.Valid(data), "Encoded: %s", data)
+
+	var typ2 OneOfWithNullable
+	require.NoError(t, typ2.Decode(jx.DecodeBytes(data)))
+}
 func TestOnePropertyObject_EncodeDecode(t *testing.T) {
 	var typ OnePropertyObject
 	typ.SetFake()
@@ -739,6 +775,30 @@ func TestTestMultipartUploadOK_EncodeDecode(t *testing.T) {
 	var typ2 TestMultipartUploadOK
 	require.NoError(t, typ2.Decode(jx.DecodeBytes(data)))
 }
+func TestTestNullableOneofsApplicationJSONCreated_EncodeDecode(t *testing.T) {
+	var typ TestNullableOneofsApplicationJSONCreated
+	typ.SetFake()
+
+	e := jx.Encoder{}
+	typ.Encode(&e)
+	data := e.Bytes()
+	require.True(t, std.Valid(data), "Encoded: %s", data)
+
+	var typ2 TestNullableOneofsApplicationJSONCreated
+	require.NoError(t, typ2.Decode(jx.DecodeBytes(data)))
+}
+func TestTestNullableOneofsApplicationJSONOK_EncodeDecode(t *testing.T) {
+	var typ TestNullableOneofsApplicationJSONOK
+	typ.SetFake()
+
+	e := jx.Encoder{}
+	typ.Encode(&e)
+	data := e.Bytes()
+	require.True(t, std.Valid(data), "Encoded: %s", data)
+
+	var typ2 TestNullableOneofsApplicationJSONOK
+	require.NoError(t, typ2.Decode(jx.DecodeBytes(data)))
+}
 func TestTestObjectQueryParameterOK_EncodeDecode(t *testing.T) {
 	var typ TestObjectQueryParameterOK
 	typ.SetFake()
diff --git a/json_test.go b/json_test.go
index f72146fe..bf3926cd 100644
--- a/json_test.go
+++ b/json_test.go
@@ -888,6 +888,84 @@ func TestJSONSum(t *testing.T) {
 			})
 		}
 	})
+	t.Run("NullableOneofs", func(t *testing.T) {
+		t.Run("OneOfWithNullable", func(t *testing.T) {
+			for i, tc := range []struct {
+				Input    string
+				Expected api.OneOfWithNullable
+				Error    bool
+			}{
+				{`10`, api.NewIntOneOfWithNullable(10), false},
+				{`"foo"`, api.NewStringOneOfWithNullable("foo"), false},
+				{`["foo", "bar"]`, api.NewStringArrayOneOfWithNullable([]string{"foo", "bar"}), false},
+				{`null`, api.NewNullOneOfWithNullable(struct{}{}), false},
+			} {
+				// Make range value copy to prevent data races.
+				tc := tc
+				t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) {
+					checker := require.NoError
+					if tc.Error {
+						checker = require.Error
+					}
+					r := api.OneOfWithNullable{}
+					checker(t, r.Decode(jx.DecodeStr(tc.Input)))
+					expected, val := tc.Expected, r
+					require.Equal(t, expected.Type, val.Type)
+				})
+			}
+		})
+		t.Run("OneOfNullables", func(t *testing.T) {
+			for i, tc := range []struct {
+				Input    string
+				Expected api.OneOfNullables
+				Error    bool
+			}{
+				{`10`, api.NewIntOneOfNullables(10), false},
+				{`"foo"`, api.NewStringOneOfNullables("foo"), false},
+				{`["foo", "bar"]`, api.NewStringArrayOneOfNullables([]string{"foo", "bar"}), false},
+				{`null`, api.NewNullOneOfNullables(struct{}{}), false},
+			} {
+				// Make range value copy to prevent data races.
+				tc := tc
+				t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) {
+					checker := require.NoError
+					if tc.Error {
+						checker = require.Error
+					}
+					r := api.OneOfNullables{}
+					checker(t, r.Decode(jx.DecodeStr(tc.Input)))
+					expected, val := tc.Expected, r
+					require.Equal(t, expected.Type, val.Type)
+				})
+			}
+		})
+		t.Run("OneOfBooleanSumNullables", func(t *testing.T) {
+			for i, tc := range []struct {
+				Input    string
+				Expected api.OneOfBooleanSumNullables
+				Error    bool
+			}{
+				{`true`, api.NewBoolOneOfBooleanSumNullables(true), false},
+				{`10`, api.NewOneOfNullablesOneOfBooleanSumNullables(api.NewIntOneOfNullables(5)), false},
+				{`"foo"`, api.NewOneOfNullablesOneOfBooleanSumNullables(api.NewNullOneOfNullables(struct{}{})), false},
+				{`["foo", "bar"]`, api.NewOneOfNullablesOneOfBooleanSumNullables(api.NewStringArrayOneOfNullables([]string{"foo", "bar"})), false},
+				{`null`, api.NewOneOfNullablesOneOfBooleanSumNullables(api.NewNullOneOfNullables(struct{}{})), false},
+			} {
+				// Make range value copy to prevent data races.
+				tc := tc
+				t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) {
+					checker := require.NoError
+					if tc.Error {
+						checker = require.Error
+					}
+					r := api.OneOfBooleanSumNullables{}
+					checker(t, r.Decode(jx.DecodeStr(tc.Input)))
+					expected, val := tc.Expected, r
+					require.Equal(t, expected.Type, val.Type)
+				})
+			}
+		})
+	})
 	t.Run("Discriminator", func(t *testing.T) {
 		for i, tc := range []struct {
 			Input    string
@@ -967,7 +1045,8 @@ func TestJSONAny(t *testing.T) {
 			Error:  false,
 		},
 
-		{Name: "AnyArray",
+		{
+			Name:   "AnyArray",
 			Format: `{"any_array":[%s]}`,
 			Error:  false,
 		},