Repository: getkin/kin-openapi
Pull request #1033 (2024-11-26 16:47:04)

openapi3: use Ptr instead of BoolPtr,Float64Ptr,Int64Ptr,Uint64Ptr
This adds generic `openapi3.Ptr` function to be used instead of `openapi3.BoolPtr, openapi3.Float64Ptr, openapi3.Int64Ptr, openapi3.Uint64Ptr`.

fenollp: I fail to see the improvement here. Today's explicitly typed functions make it much easier to grasp their purpose and to find IMO. Thoughts?
alexandear: > I fail to see the improvement here. Today's explicitly typed functions make it much easier to grasp their purpose and to find IMO. Thoughts?

I intend to introduce' Ptr' to shorten `BoolPtr`, `Float64Ptr`, `Int64Ptr`, `Uint64Ptr`. Fewer symbols - same meaning. And this reduces the number of functions to remember for the library users.

See these PRs where `Ptr` was recently introduced:

- https://github.com/xanzy/go-gitlab/pull/1788
- https://github.com/google/go-github/pull/3355 (not yet merged, but likely to be merged soon)

Patch:
diff --git a/.github/docs/openapi3.txt b/.github/docs/openapi3.txt
index 4729e0c..7d0fa44 100644
--- a/.github/docs/openapi3.txt
+++ b/.github/docs/openapi3.txt
@@ -35,10 +35,10 @@ const (
 	FormatOfStringByte = `(^$|^[a-zA-Z0-9+/\-_]*=*$)`
 
 	// FormatOfStringDate is a RFC3339 date format regexp, for example "2017-07-21".
-	FormatOfStringDate = `^[0-9]{4}-(0[0-9]|10|11|12)-([0-2][0-9]|30|31)$`
+	FormatOfStringDate = `^[0-9]{4}-(0[1-9]|10|11|12)-(0[1-9]|[12][0-9]|3[01])$`
 
 	// FormatOfStringDateTime is a RFC3339 date-time format regexp, for example "2017-07-21T17:32:28Z".
-	FormatOfStringDateTime = `^[0-9]{4}-(0[0-9]|10|11|12)-([0-2][0-9]|30|31)T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|(\+|-)[0-9]{2}:[0-9]{2})?$`
+	FormatOfStringDateTime = `^[0-9]{4}-(0[1-9]|10|11|12)-(0[1-9]|[12][0-9]|3[01])T([0-1][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)[0-9]{2}:[0-9]{2})?$`
 )
 const (
 	SerializationSimple         = "simple"
@@ -90,12 +90,19 @@ var ErrURINotSupported = errors.New("unsupported URI")
     ErrURINotSupported indicates the ReadFromURIFunc does not know how to handle
     a given URI.
 
+var IncludeOrigin = false
+    IncludeOrigin specifies whether to include the origin of the OpenAPI
+    elements Set this to true before loading a spec to include the origin of the
+    OpenAPI elements Note it is global and affects all loaders
+
 
 FUNCTIONS
 
 func BoolPtr(value bool) *bool
     BoolPtr is a helper for defining OpenAPI schemas.
 
+    Deprecated: Use Ptr instead.
+
 func DefaultRefNameResolver(doc *T, ref ComponentRef) string
     DefaultRefResolver is a default implementation of refNameResolver for the
     InternalizeRefs function.
@@ -147,9 +154,16 @@ func DefineStringFormatValidator(name string, validator StringFormatValidator)
 func Float64Ptr(value float64) *float64
     Float64Ptr is a helper for defining OpenAPI schemas.
 
+    Deprecated: Use Ptr instead.
+
 func Int64Ptr(value int64) *int64
     Int64Ptr is a helper for defining OpenAPI schemas.
 
+    Deprecated: Use Ptr instead.
+
+func Ptr[T any](value T) *T
+    Ptr is a helper for defining OpenAPI schemas.
+
 func ReadFromFile(loader *Loader, location *url.URL) ([]byte, error)
     ReadFromFile is a ReadFromURIFunc which reads local file URIs.
 
@@ -202,6 +216,8 @@ func RegisterArrayUniqueItemsChecker(fn SliceUniqueItemsChecker)
 func Uint64Ptr(value uint64) *uint64
     Uint64Ptr is a helper for defining OpenAPI schemas.
 
+    Deprecated: Use Ptr instead.
+
 func ValidateIdentifier(value string) error
     ValidateIdentifier returns an error if the given component name does not
     match IdentifierRegExp.
@@ -229,7 +245,7 @@ func (addProps *AdditionalProperties) UnmarshalJSON(data []byte) error
 
 type Callback struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	// Has unexported fields.
 }
@@ -332,7 +348,7 @@ type ComponentRef interface {
 
 type Components struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Schemas         Schemas         `json:"schemas,omitempty" yaml:"schemas,omitempty"`
 	Parameters      ParametersMap   `json:"parameters,omitempty" yaml:"parameters,omitempty"`
@@ -364,7 +380,7 @@ func (components *Components) Validate(ctx context.Context, opts ...ValidationOp
 
 type Contact struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Name  string `json:"name,omitempty" yaml:"name,omitempty"`
 	URL   string `json:"url,omitempty" yaml:"url,omitempty"`
@@ -412,7 +428,7 @@ func (content Content) Validate(ctx context.Context, opts ...ValidationOption) e
 
 type Discriminator struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	PropertyName string    `json:"propertyName" yaml:"propertyName"` // required
 	Mapping      StringMap `json:"mapping,omitempty" yaml:"mapping,omitempty"`
@@ -435,7 +451,7 @@ func (discriminator *Discriminator) Validate(ctx context.Context, opts ...Valida
 
 type Encoding struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	ContentType   string  `json:"contentType,omitempty" yaml:"contentType,omitempty"`
 	Headers       Headers `json:"headers,omitempty" yaml:"headers,omitempty"`
@@ -471,7 +487,7 @@ func (encoding *Encoding) WithHeaderRef(name string, ref *HeaderRef) *Encoding
 
 type Example struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Summary       string `json:"summary,omitempty" yaml:"summary,omitempty"`
 	Description   string `json:"description,omitempty" yaml:"description,omitempty"`
@@ -547,7 +563,7 @@ func (examples *Examples) UnmarshalJSON(data []byte) (err error)
 
 type ExternalDocs struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Description string `json:"description,omitempty" yaml:"description,omitempty"`
 	URL         string `json:"url,omitempty" yaml:"url,omitempty"`
@@ -662,7 +678,7 @@ func (headers *Headers) UnmarshalJSON(data []byte) (err error)
 
 type Info struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Title          string   `json:"title" yaml:"title"` // Required
 	Description    string   `json:"description,omitempty" yaml:"description,omitempty"`
@@ -691,7 +707,7 @@ type IntegerFormatValidator = FormatValidator[int64]
 
 type License struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Name string `json:"name" yaml:"name"` // Required
 	URL  string `json:"url,omitempty" yaml:"url,omitempty"`
@@ -713,7 +729,7 @@ func (license *License) Validate(ctx context.Context, opts ...ValidationOption)
 
 type Link struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	OperationRef string         `json:"operationRef,omitempty" yaml:"operationRef,omitempty"`
 	OperationID  string         `json:"operationId,omitempty" yaml:"operationId,omitempty"`
@@ -790,9 +806,6 @@ type Loader struct {
 	// IsExternalRefsAllowed enables visiting other files
 	IsExternalRefsAllowed bool
 
-	// IncludeOrigin specifies whether to include the origin of the OpenAPI elements
-	IncludeOrigin bool
-
 	// ReadFromURIFunc allows overriding the any file/URL reading func
 	ReadFromURIFunc ReadFromURIFunc
 
@@ -836,7 +849,7 @@ type Location struct {
 
 type MediaType struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Schema   *SchemaRef           `json:"schema,omitempty" yaml:"schema,omitempty"`
 	Example  any                  `json:"example,omitempty" yaml:"example,omitempty"`
@@ -914,7 +927,7 @@ type NumberFormatValidator = FormatValidator[float64]
 
 type OAuthFlow struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	AuthorizationURL string    `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"`
 	TokenURL         string    `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"`
@@ -939,7 +952,7 @@ func (flow *OAuthFlow) Validate(ctx context.Context, opts ...ValidationOption) e
 
 type OAuthFlows struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Implicit          *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"`
 	Password          *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"`
@@ -964,7 +977,7 @@ func (flows *OAuthFlows) Validate(ctx context.Context, opts ...ValidationOption)
 
 type Operation struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	// Optional tags for documentation.
 	Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
@@ -1037,7 +1050,7 @@ type Origin struct {
 
 type Parameter struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Name            string     `json:"name,omitempty" yaml:"name,omitempty"`
 	In              string     `json:"in,omitempty" yaml:"in,omitempty"`
@@ -1159,7 +1172,7 @@ func (parametersMap *ParametersMap) UnmarshalJSON(data []byte) (err error)
 
 type PathItem struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Ref         string     `json:"$ref,omitempty" yaml:"$ref,omitempty"`
 	Summary     string     `json:"summary,omitempty" yaml:"summary,omitempty"`
@@ -1199,7 +1212,7 @@ func (pathItem *PathItem) Validate(ctx context.Context, opts ...ValidationOption
 
 type Paths struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	// Has unexported fields.
 }
@@ -1289,7 +1302,7 @@ func URIMapCache(reader ReadFromURIFunc) ReadFromURIFunc
 
 type Ref struct {
 	Ref    string  `json:"$ref" yaml:"$ref"`
-	Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 }
     Ref is specified by OpenAPI/Swagger 3.0 standard. See
     https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#reference-object
@@ -1318,7 +1331,7 @@ func (requestBodies *RequestBodies) UnmarshalJSON(data []byte) (err error)
 
 type RequestBody struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Description string  `json:"description,omitempty" yaml:"description,omitempty"`
 	Required    bool    `json:"required,omitempty" yaml:"required,omitempty"`
@@ -1405,7 +1418,7 @@ func (x *RequestBodyRef) Validate(ctx context.Context, opts ...ValidationOption)
 
 type Response struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Description *string `json:"description,omitempty" yaml:"description,omitempty"`
 	Headers     Headers `json:"headers,omitempty" yaml:"headers,omitempty"`
@@ -1548,7 +1561,7 @@ func (responses *Responses) Value(key string) *ResponseRef
 
 type Schema struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	OneOf        SchemaRefs    `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
 	AnyOf        SchemaRefs    `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
@@ -1888,7 +1901,7 @@ func (srs *SecurityRequirements) With(securityRequirement SecurityRequirement) *
 
 type SecurityScheme struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Type             string      `json:"type,omitempty" yaml:"type,omitempty"`
 	Description      string      `json:"description,omitempty" yaml:"description,omitempty"`
@@ -1995,7 +2008,7 @@ type SerializationMethod struct {
 
 type Server struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	URL         string                     `json:"url" yaml:"url"` // Required
 	Description string                     `json:"description,omitempty" yaml:"description,omitempty"`
@@ -2026,7 +2039,7 @@ func (server *Server) Validate(ctx context.Context, opts ...ValidationOption) (e
 
 type ServerVariable struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Enum        []string `json:"enum,omitempty" yaml:"enum,omitempty"`
 	Default     string   `json:"default,omitempty" yaml:"default,omitempty"`
@@ -2132,7 +2145,7 @@ func (doc *T) Validate(ctx context.Context, opts ...ValidationOption) error
 
 type Tag struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Name         string        `json:"name,omitempty" yaml:"name,omitempty"`
 	Description  string        `json:"description,omitempty" yaml:"description,omitempty"`
@@ -2244,7 +2257,7 @@ type ValidationOptions struct {
 
 type XML struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Name      string `json:"name,omitempty" yaml:"name,omitempty"`
 	Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
diff --git a/.github/docs/openapi3filter.txt b/.github/docs/openapi3filter.txt
index d44ba5a..42c6249 100644
--- a/.github/docs/openapi3filter.txt
+++ b/.github/docs/openapi3filter.txt
@@ -42,6 +42,9 @@ FUNCTIONS
 func ConvertErrors(err error) error
     ConvertErrors converts all errors to the appropriate error format.
 
+func CsvBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error)
+    CsvBodyDecoder is a body decoder that decodes a csv body to a string.
+
 func DefaultErrorEncoder(_ context.Context, err error, w http.ResponseWriter)
     DefaultErrorEncoder writes the error to the ResponseWriter, by default a
     content type of text/plain, a body of the plain text of the error, and a
@@ -58,9 +61,11 @@ func JSONBodyDecoder(body io.Reader, header http.Header, schema *openapi3.Schema
     JSONBodyDecoder decodes a JSON formatted body. It is public so that is easy
     to register additional JSON based formats.
 
+func MultipartBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error)
 func NoopAuthenticationFunc(context.Context, *AuthenticationInput) error
     NoopAuthenticationFunc is an AuthenticationFunc
 
+func PlainBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error)
 func RegisterBodyDecoder(contentType string, decoder BodyDecoder)
     RegisterBodyDecoder registers a request body's decoder for a content type.
 
@@ -84,6 +89,7 @@ func UnregisterBodyDecoder(contentType string)
 func UnregisterBodyEncoder(contentType string)
     UnregisterBodyEncoder disables package-wide decoding of contentType values
 
+func UrlencodedBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error)
 func ValidateParameter(ctx context.Context, input *RequestValidationInput, parameter *openapi3.Parameter) error
     ValidateParameter validates a parameter's value by JSON schema. The function
     returns RequestError with a ParseError cause when unable to parse a value.
@@ -93,7 +99,7 @@ func ValidateParameter(ctx context.Context, input *RequestValidationInput, param
     defined. The function returns RequestError with a openapi3.SchemaError cause
     when a value is invalid by JSON schema.
 
-func ValidateRequest(ctx context.Context, input *RequestValidationInput) (err error)
+func ValidateRequest(ctx context.Context, input *RequestValidationInput) error
     ValidateRequest is used to validate the given input according to previous
     loaded OpenAPIv3 spec. If the input does not match the OpenAPIv3 spec,
     a non-nil error will be returned.
@@ -121,6 +127,12 @@ func ValidateSecurityRequirements(ctx context.Context, input *RequestValidationI
     requirements in order and returns nil on the first valid requirement.
     If no requirement is met, errors are returned in order.
 
+func YamlBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error)
+func ZipFileBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error)
+    ZipFileBodyDecoder is a body decoder that decodes a zip file body to a
+    string. Use with caution as this implementation may be susceptible to a zip
+    bomb attack.
+
 
 TYPES
 
diff --git a/README.md b/README.md
index d135ffe..00c75b2 100644
--- a/README.md
+++ b/README.md
@@ -130,8 +130,7 @@ func main() {
 
 ## Custom content type for body of HTTP request/response
 
-By default, the library parses a body of the HTTP request and response
-if it has one of the following content types: `"text/plain"` or `"application/json"`.
+By default, the library parses a body of the HTTP request and response of [a few content types](https://github.com/getkin/kin-openapi/blob/6da871e0e170b7637eb568c265c08bc2b5d6e7a3/openapi3filter/req_resp_decoder.go#L1264) e.g. `"text/plain"` or `"application/json"`.
 To support other content types you must register decoders for them:
 
 ```go
@@ -295,6 +294,9 @@ for _, path := range doc.Paths.InMatchingOrder() {
 
 ## CHANGELOG: Sub-v1 breaking API changes
 
+### v0.131.0
+* No longer `openapi3filter.RegisterBodyDecoder` the `openapi3filter.ZipFileBodyDecoder` by default.
+
 ### v0.129.0
 * `openapi3.Discriminator.Mapping` and `openapi3.OAuthFlow.Scopes` fields went from a `map[string]string` to the new type `StringMap`
 
diff --git a/cmd/validate/main.go b/cmd/validate/main.go
index 874f30c..7519407 100644
--- a/cmd/validate/main.go
+++ b/cmd/validate/main.go
@@ -6,7 +6,7 @@ import (
 	"os"
 	"strings"
 
-	"github.com/invopop/yaml"
+	"github.com/oasdiff/yaml"
 
 	"github.com/getkin/kin-openapi/openapi2"
 	"github.com/getkin/kin-openapi/openapi3"
diff --git a/go.mod b/go.mod
index 7d37032..6f53d9f 100644
--- a/go.mod
+++ b/go.mod
@@ -2,18 +2,15 @@ module github.com/getkin/kin-openapi
 
 go 1.22.5
 
-replace gopkg.in/yaml.v3 => github.com/oasdiff/yaml3 v0.0.0-20240920135353-c185dc6ea7c6
-
-replace github.com/invopop/yaml => github.com/oasdiff/yaml v0.0.0-20240920191703-3e5a9fb5bdf3
-
 require (
 	github.com/go-openapi/jsonpointer v0.21.0
 	github.com/gorilla/mux v1.8.0
-	github.com/invopop/yaml v0.3.1
 	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
+	github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037
+	github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90
 	github.com/perimeterx/marshmallow v1.1.5
 	github.com/stretchr/testify v1.9.0
-	gopkg.in/yaml.v3 v3.0.1
+	github.com/woodsbury/decimal128 v1.3.0
 )
 
 require (
@@ -23,4 +20,5 @@ require (
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/rogpeppe/go-internal v1.12.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/go.sum b/go.sum
index f9643cd..34001ef 100644
--- a/go.sum
+++ b/go.sum
@@ -18,10 +18,10 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0
 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
-github.com/oasdiff/yaml v0.0.0-20240920191703-3e5a9fb5bdf3 h1:nqCxALSUgWobWkFGIrhLRzR/bpImQdGj+3JS4/scTJo=
-github.com/oasdiff/yaml v0.0.0-20240920191703-3e5a9fb5bdf3/go.mod h1:AOyUNV9ElKz7EEZeBm/48U54UtjtgCMT9fFbZEsClQc=
-github.com/oasdiff/yaml3 v0.0.0-20240920135353-c185dc6ea7c6 h1:+ZsuDTdapTJxfMQk7SOJiNMg0v36pui01L7FEO615r8=
-github.com/oasdiff/yaml3 v0.0.0-20240920135353-c185dc6ea7c6/go.mod h1:lqlOfJRrYpgeWHQj+ky2tf7UJ3PzgHTHRQEpc90nbp0=
+github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
+github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
+github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
+github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
 github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
 github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -32,5 +32,10 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
 github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
 github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
+github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0=
+github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/openapi2/marsh.go b/openapi2/marsh.go
index 1a4b0d1..1745734 100644
--- a/openapi2/marsh.go
+++ b/openapi2/marsh.go
@@ -5,7 +5,7 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/invopop/yaml"
+	"github.com/oasdiff/yaml"
 )
 
 func unmarshalError(jsonUnmarshalErr error) error {
diff --git a/openapi2conv/openapi2_conv.go b/openapi2conv/openapi2_conv.go
index ec7646d..4bb051b 100644
--- a/openapi2conv/openapi2_conv.go
+++ b/openapi2conv/openapi2_conv.go
@@ -149,12 +149,13 @@ func ToV3Operation(doc2 *openapi2.T, components *openapi3.Components, pathItem *
 		return nil, nil
 	}
 	doc3 := &openapi3.Operation{
-		OperationID: operation.OperationID,
-		Summary:     operation.Summary,
-		Description: operation.Description,
-		Deprecated:  operation.Deprecated,
-		Tags:        operation.Tags,
-		Extensions:  stripNonExtensions(operation.Extensions),
+		OperationID:  operation.OperationID,
+		Summary:      operation.Summary,
+		Description:  operation.Description,
+		Deprecated:   operation.Deprecated,
+		Tags:         operation.Tags,
+		Extensions:   stripNonExtensions(operation.Extensions),
+		ExternalDocs: operation.ExternalDocs,
 	}
 	if v := operation.Security; v != nil {
 		doc3Security := ToV3SecurityRequirements(*v)
@@ -513,11 +514,7 @@ func ToV3SchemaRef(schema *openapi2.SchemaRef) *openapi3.SchemaRef {
 		MaxProps:             schema.Value.MaxProps,
 		AllOf:                make(openapi3.SchemaRefs, len(schema.Value.AllOf)),
 		Properties:           make(openapi3.Schemas),
-		AdditionalProperties: schema.Value.AdditionalProperties,
-	}
-
-	if schema.Value.AdditionalProperties.Schema != nil {
-		v3Schema.AdditionalProperties.Schema.Ref = ToV3Ref(schema.Value.AdditionalProperties.Schema.Ref)
+		AdditionalProperties: toV3AdditionalProperties(schema.Value.AdditionalProperties),
 	}
 
 	if schema.Value.Discriminator != "" {
@@ -551,6 +548,30 @@ func ToV3SchemaRef(schema *openapi2.SchemaRef) *openapi3.SchemaRef {
 	}
 }
 
+func toV3AdditionalProperties(from openapi3.AdditionalProperties) openapi3.AdditionalProperties {
+	return openapi3.AdditionalProperties{
+		Has:    from.Has,
+		Schema: convertRefsInV3SchemaRef(from.Schema),
+	}
+}
+
+func convertRefsInV3SchemaRef(from *openapi3.SchemaRef) *openapi3.SchemaRef {
+	if from == nil {
+		return nil
+	}
+	to := *from
+	to.Ref = ToV3Ref(to.Ref)
+	if to.Value != nil {
+		v := *from.Value
+		to.Value = &v
+		if to.Value.Items != nil {
+			to.Value.Items.Ref = ToV3Ref(to.Value.Items.Ref)
+		}
+		to.Value.AdditionalProperties = toV3AdditionalProperties(to.Value.AdditionalProperties)
+	}
+	return &to
+}
+
 var ref2To3 = map[string]string{
 	"#/definitions/": "#/components/schemas/",
 	"#/responses/":   "#/components/responses/",
@@ -1045,12 +1066,13 @@ func FromV3Operation(doc3 *openapi3.T, operation *openapi3.Operation) (*openapi2
 		return nil, nil
 	}
 	result := &openapi2.Operation{
-		OperationID: operation.OperationID,
-		Summary:     operation.Summary,
-		Description: operation.Description,
-		Deprecated:  operation.Deprecated,
-		Tags:        operation.Tags,
-		Extensions:  stripNonExtensions(operation.Extensions),
+		OperationID:  operation.OperationID,
+		Summary:      operation.Summary,
+		Description:  operation.Description,
+		Deprecated:   operation.Deprecated,
+		Tags:         operation.Tags,
+		Extensions:   stripNonExtensions(operation.Extensions),
+		ExternalDocs: operation.ExternalDocs,
 	}
 	if v := operation.Security; v != nil {
 		resultSecurity := FromV3SecurityRequirements(*v)
diff --git a/openapi3/callback.go b/openapi3/callback.go
index 3ae6da6..31f4e4b 100644
--- a/openapi3/callback.go
+++ b/openapi3/callback.go
@@ -9,7 +9,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#callback-object
 type Callback struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	m map[string]*PathItem
 }
diff --git a/openapi3/components.go b/openapi3/components.go
index aecf8b6..890671a 100644
--- a/openapi3/components.go
+++ b/openapi3/components.go
@@ -25,7 +25,7 @@ type (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#components-object
 type Components struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Schemas         Schemas         `json:"schemas,omitempty" yaml:"schemas,omitempty"`
 	Parameters      ParametersMap   `json:"parameters,omitempty" yaml:"parameters,omitempty"`
diff --git a/openapi3/contact.go b/openapi3/contact.go
index 57cc801..2ade3c6 100644
--- a/openapi3/contact.go
+++ b/openapi3/contact.go
@@ -9,7 +9,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#contact-object
 type Contact struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Name  string `json:"name,omitempty" yaml:"name,omitempty"`
 	URL   string `json:"url,omitempty" yaml:"url,omitempty"`
diff --git a/openapi3/discriminator.go b/openapi3/discriminator.go
index a8ab07b..6934c17 100644
--- a/openapi3/discriminator.go
+++ b/openapi3/discriminator.go
@@ -9,7 +9,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#discriminator-object
 type Discriminator struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	PropertyName string    `json:"propertyName" yaml:"propertyName"` // required
 	Mapping      StringMap `json:"mapping,omitempty" yaml:"mapping,omitempty"`
diff --git a/openapi3/encoding.go b/openapi3/encoding.go
index 7eb507e..113eb73 100644
--- a/openapi3/encoding.go
+++ b/openapi3/encoding.go
@@ -11,7 +11,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#encoding-object
 type Encoding struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	ContentType   string  `json:"contentType,omitempty" yaml:"contentType,omitempty"`
 	Headers       Headers `json:"headers,omitempty" yaml:"headers,omitempty"`
diff --git a/openapi3/example.go b/openapi3/example.go
index 56aee37..44d5777 100644
--- a/openapi3/example.go
+++ b/openapi3/example.go
@@ -10,7 +10,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#example-object
 type Example struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Summary       string `json:"summary,omitempty" yaml:"summary,omitempty"`
 	Description   string `json:"description,omitempty" yaml:"description,omitempty"`
diff --git a/openapi3/external_docs.go b/openapi3/external_docs.go
index f679414..7ff435f 100644
--- a/openapi3/external_docs.go
+++ b/openapi3/external_docs.go
@@ -12,7 +12,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#external-documentation-object
 type ExternalDocs struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Description string `json:"description,omitempty" yaml:"description,omitempty"`
 	URL         string `json:"url,omitempty" yaml:"url,omitempty"`
diff --git a/openapi3/helpers.go b/openapi3/helpers.go
index d50b3d8..b36b0b7 100644
--- a/openapi3/helpers.go
+++ b/openapi3/helpers.go
@@ -31,22 +31,35 @@ func ValidateIdentifier(value string) error {
 	return fmt.Errorf("identifier %q is not supported by OpenAPIv3 standard (charset: [%q])", value, identifierChars)
 }
 
+// Ptr is a helper for defining OpenAPI schemas.
+func Ptr[T any](value T) *T {
+	return &value
+}
+
 // Float64Ptr is a helper for defining OpenAPI schemas.
+//
+// Deprecated: Use Ptr instead.
 func Float64Ptr(value float64) *float64 {
 	return &value
 }
 
 // BoolPtr is a helper for defining OpenAPI schemas.
+//
+// Deprecated: Use Ptr instead.
 func BoolPtr(value bool) *bool {
 	return &value
 }
 
 // Int64Ptr is a helper for defining OpenAPI schemas.
+//
+// Deprecated: Use Ptr instead.
 func Int64Ptr(value int64) *int64 {
 	return &value
 }
 
 // Uint64Ptr is a helper for defining OpenAPI schemas.
+//
+// Deprecated: Use Ptr instead.
 func Uint64Ptr(value uint64) *uint64 {
 	return &value
 }
diff --git a/openapi3/info.go b/openapi3/info.go
index acca2f4..ed5710e 100644
--- a/openapi3/info.go
+++ b/openapi3/info.go
@@ -10,7 +10,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#info-object
 type Info struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Title          string   `json:"title" yaml:"title"` // Required
 	Description    string   `json:"description,omitempty" yaml:"description,omitempty"`
diff --git a/openapi3/license.go b/openapi3/license.go
index df9d6c4..eb47fc3 100644
--- a/openapi3/license.go
+++ b/openapi3/license.go
@@ -10,7 +10,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#license-object
 type License struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Name string `json:"name" yaml:"name"` // Required
 	URL  string `json:"url,omitempty" yaml:"url,omitempty"`
diff --git a/openapi3/link.go b/openapi3/link.go
index dfd320f..c76cf44 100644
--- a/openapi3/link.go
+++ b/openapi3/link.go
@@ -11,7 +11,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#link-object
 type Link struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	OperationRef string         `json:"operationRef,omitempty" yaml:"operationRef,omitempty"`
 	OperationID  string         `json:"operationId,omitempty" yaml:"operationId,omitempty"`
diff --git a/openapi3/loader.go b/openapi3/loader.go
index 0f3b8cb..436a1b3 100644
--- a/openapi3/loader.go
+++ b/openapi3/loader.go
@@ -15,6 +15,11 @@ import (
 	"strings"
 )
 
+// IncludeOrigin specifies whether to include the origin of the OpenAPI elements
+// Set this to true before loading a spec to include the origin of the OpenAPI elements
+// Note it is global and affects all loaders
+var IncludeOrigin = false
+
 func foundUnresolvedRef(ref string) error {
 	return fmt.Errorf("found unresolved ref: %q", ref)
 }
@@ -28,9 +33,6 @@ type Loader struct {
 	// IsExternalRefsAllowed enables visiting other files
 	IsExternalRefsAllowed bool
 
-	// IncludeOrigin specifies whether to include the origin of the OpenAPI elements
-	IncludeOrigin bool
-
 	// ReadFromURIFunc allows overriding the any file/URL reading func
 	ReadFromURIFunc ReadFromURIFunc
 
@@ -106,7 +108,7 @@ func (loader *Loader) loadSingleElementFromURI(ref string, rootPath *url.URL, el
 	if err != nil {
 		return nil, err
 	}
-	if err := unmarshal(data, element, loader.IncludeOrigin); err != nil {
+	if err := unmarshal(data, element, IncludeOrigin); err != nil {
 		return nil, err
 	}
 
@@ -142,7 +144,7 @@ func (loader *Loader) LoadFromIoReader(reader io.Reader) (*T, error) {
 func (loader *Loader) LoadFromData(data []byte) (*T, error) {
 	loader.resetVisitedPathItemRefs()
 	doc := &T{}
-	if err := unmarshal(data, doc, loader.IncludeOrigin); err != nil {
+	if err := unmarshal(data, doc, IncludeOrigin); err != nil {
 		return nil, err
 	}
 	if err := loader.ResolveRefsIn(doc, nil); err != nil {
@@ -171,7 +173,7 @@ func (loader *Loader) loadFromDataWithPathInternal(data []byte, location *url.UR
 	doc := &T{}
 	loader.visitedDocuments[uri] = doc
 
-	if err := unmarshal(data, doc, loader.IncludeOrigin); err != nil {
+	if err := unmarshal(data, doc, IncludeOrigin); err != nil {
 		return nil, err
 	}
 
@@ -425,7 +427,7 @@ func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolv
 		if err2 != nil {
 			return nil, nil, err
 		}
-		if err2 = unmarshal(data, &cursor, loader.IncludeOrigin); err2 != nil {
+		if err2 = unmarshal(data, &cursor, IncludeOrigin); err2 != nil {
 			return nil, nil, err
 		}
 		if cursor, err2 = drill(cursor); err2 != nil || cursor == nil {
diff --git a/openapi3/loader_uri_reader.go b/openapi3/loader_uri_reader.go
index b023dfb..5dc4a9b 100644
--- a/openapi3/loader_uri_reader.go
+++ b/openapi3/loader_uri_reader.go
@@ -7,6 +7,7 @@ import (
 	"net/http"
 	"net/url"
 	"os"
+	"path"
 	"path/filepath"
 	"sync"
 )
@@ -79,7 +80,7 @@ func ReadFromFile(loader *Loader, location *url.URL) ([]byte, error) {
 	if !is_file(location) {
 		return nil, ErrURINotSupported
 	}
-	return os.ReadFile(filepath.FromSlash(location.Path))
+	return os.ReadFile(path.Clean(filepath.FromSlash(location.Path)))
 }
 
 // URIMapCache returns a ReadFromURIFunc that caches the contents read from URI
diff --git a/openapi3/marsh.go b/openapi3/marsh.go
index 9fdc6df..2f00828 100644
--- a/openapi3/marsh.go
+++ b/openapi3/marsh.go
@@ -5,7 +5,7 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/invopop/yaml"
+	"github.com/oasdiff/yaml"
 )
 
 func unmarshalError(jsonUnmarshalErr error) error {
diff --git a/openapi3/media_type.go b/openapi3/media_type.go
index d6edaf4..576826f 100644
--- a/openapi3/media_type.go
+++ b/openapi3/media_type.go
@@ -14,7 +14,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#media-type-object
 type MediaType struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Schema   *SchemaRef           `json:"schema,omitempty" yaml:"schema,omitempty"`
 	Example  any                  `json:"example,omitempty" yaml:"example,omitempty"`
diff --git a/openapi3/operation.go b/openapi3/operation.go
index 7b57e84..f32836e 100644
--- a/openapi3/operation.go
+++ b/openapi3/operation.go
@@ -14,7 +14,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object
 type Operation struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	// Optional tags for documentation.
 	Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
diff --git a/openapi3/origin.go b/openapi3/origin.go
index b0e4a93..d2a51d9 100644
--- a/openapi3/origin.go
+++ b/openapi3/origin.go
@@ -1,6 +1,6 @@
 package openapi3
 
-const originKey = "origin"
+const originKey = "__origin__"
 
 // Origin contains the origin of a collection.
 // Key is the location of the collection itself.
diff --git a/openapi3/parameter.go b/openapi3/parameter.go
index d69bd5f..23582f7 100644
--- a/openapi3/parameter.go
+++ b/openapi3/parameter.go
@@ -73,7 +73,7 @@ func (parameters Parameters) Validate(ctx context.Context, opts ...ValidationOpt
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#parameter-object
 type Parameter struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Name            string     `json:"name,omitempty" yaml:"name,omitempty"`
 	In              string     `json:"in,omitempty" yaml:"in,omitempty"`
diff --git a/openapi3/path_item.go b/openapi3/path_item.go
index 2c60472..9839786 100644
--- a/openapi3/path_item.go
+++ b/openapi3/path_item.go
@@ -12,7 +12,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#path-item-object
 type PathItem struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Ref         string     `json:"$ref,omitempty" yaml:"$ref,omitempty"`
 	Summary     string     `json:"summary,omitempty" yaml:"summary,omitempty"`
diff --git a/openapi3/paths.go b/openapi3/paths.go
index 8067504..edd8c87 100644
--- a/openapi3/paths.go
+++ b/openapi3/paths.go
@@ -11,7 +11,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#paths-object
 type Paths struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	m map[string]*PathItem
 }
diff --git a/openapi3/ref.go b/openapi3/ref.go
index d24f4c9..83893b6 100644
--- a/openapi3/ref.go
+++ b/openapi3/ref.go
@@ -6,5 +6,5 @@ package openapi3
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#reference-object
 type Ref struct {
 	Ref    string  `json:"$ref" yaml:"$ref"`
-	Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 }
diff --git a/openapi3/request_body.go b/openapi3/request_body.go
index 39288ee..6c15ba0 100644
--- a/openapi3/request_body.go
+++ b/openapi3/request_body.go
@@ -10,7 +10,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#request-body-object
 type RequestBody struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Description string  `json:"description,omitempty" yaml:"description,omitempty"`
 	Required    bool    `json:"required,omitempty" yaml:"required,omitempty"`
diff --git a/openapi3/response.go b/openapi3/response.go
index d4bfe42..b87324a 100644
--- a/openapi3/response.go
+++ b/openapi3/response.go
@@ -103,7 +103,7 @@ func (responses *Responses) Validate(ctx context.Context, opts ...ValidationOpti
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#response-object
 type Response struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Description *string `json:"description,omitempty" yaml:"description,omitempty"`
 	Headers     Headers `json:"headers,omitempty" yaml:"headers,omitempty"`
diff --git a/openapi3/schema.go b/openapi3/schema.go
index 10a00ea..a0addb5 100644
--- a/openapi3/schema.go
+++ b/openapi3/schema.go
@@ -17,6 +17,7 @@ import (
 
 	"github.com/go-openapi/jsonpointer"
 	"github.com/mohae/deepcopy"
+	"github.com/woodsbury/decimal128"
 )
 
 const (
@@ -81,7 +82,7 @@ func (s SchemaRefs) JSONLookup(token string) (any, error) {
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#schema-object
 type Schema struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	OneOf        SchemaRefs    `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
 	AnyOf        SchemaRefs    `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
@@ -837,12 +838,12 @@ func (schema *Schema) WithMaxProperties(i int64) *Schema {
 }
 
 func (schema *Schema) WithAnyAdditionalProperties() *Schema {
-	schema.AdditionalProperties = AdditionalProperties{Has: BoolPtr(true)}
+	schema.AdditionalProperties = AdditionalProperties{Has: Ptr(true)}
 	return schema
 }
 
 func (schema *Schema) WithoutAdditionalProperties() *Schema {
-	schema.AdditionalProperties = AdditionalProperties{Has: BoolPtr(false)}
+	schema.AdditionalProperties = AdditionalProperties{Has: Ptr(false)}
 	return schema
 }
 
@@ -1643,7 +1644,10 @@ func (schema *Schema) visitJSONNumber(settings *schemaValidationSettings, value
 	if v := schema.MultipleOf; v != nil {
 		// "A numeric instance is valid only if division by this keyword's
 		//    value results in an integer."
-		if bigFloat := big.NewFloat(value / *v); !bigFloat.IsInt() {
+		numParsed, _ := decimal128.Parse(fmt.Sprintf("%.10f", value))
+		denParsed, _ := decimal128.Parse(fmt.Sprintf("%.10f", *v))
+		_, remainder := numParsed.QuoRem(denParsed)
+		if !remainder.IsZero() {
 			if settings.failfast {
 				return errSchema
 			}
diff --git a/openapi3/schema_formats.go b/openapi3/schema_formats.go
index 1b76707..489105b 100644
--- a/openapi3/schema_formats.go
+++ b/openapi3/schema_formats.go
@@ -41,10 +41,10 @@ const (
 	FormatOfStringByte = `(^$|^[a-zA-Z0-9+/\-_]*=*$)`
 
 	// FormatOfStringDate is a RFC3339 date format regexp, for example "2017-07-21".
-	FormatOfStringDate = `^[0-9]{4}-(0[0-9]|10|11|12)-([0-2][0-9]|30|31)$`
+	FormatOfStringDate = `^[0-9]{4}-(0[1-9]|10|11|12)-(0[1-9]|[12][0-9]|3[01])$`
 
 	// FormatOfStringDateTime is a RFC3339 date-time format regexp, for example "2017-07-21T17:32:28Z".
-	FormatOfStringDateTime = `^[0-9]{4}-(0[0-9]|10|11|12)-([0-2][0-9]|30|31)T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|(\+|-)[0-9]{2}:[0-9]{2})?$`
+	FormatOfStringDateTime = `^[0-9]{4}-(0[1-9]|10|11|12)-(0[1-9]|[12][0-9]|3[01])T([0-1][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)[0-9]{2}:[0-9]{2})?$`
 )
 
 func init() {
diff --git a/openapi3/schema_pattern.go b/openapi3/schema_pattern.go
index 5819713..2010d57 100644
--- a/openapi3/schema_pattern.go
+++ b/openapi3/schema_pattern.go
@@ -30,6 +30,6 @@ func (schema *Schema) compilePattern(c RegexCompilerFunc) (cp RegexMatcher, err
 		return
 	}
 
-	var _ bool = compiledPatterns.CompareAndSwap(pattern, nil, cp)
+	compiledPatterns.Store(pattern, cp)
 	return
 }
diff --git a/openapi3/security_scheme.go b/openapi3/security_scheme.go
index 7bc889d..676002a 100644
--- a/openapi3/security_scheme.go
+++ b/openapi3/security_scheme.go
@@ -12,7 +12,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#security-scheme-object
 type SecurityScheme struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Type             string      `json:"type,omitempty" yaml:"type,omitempty"`
 	Description      string      `json:"description,omitempty" yaml:"description,omitempty"`
@@ -218,7 +218,7 @@ func (ss *SecurityScheme) Validate(ctx context.Context, opts ...ValidationOption
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#oauth-flows-object
 type OAuthFlows struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Implicit          *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"`
 	Password          *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"`
@@ -320,7 +320,7 @@ func (flows *OAuthFlows) Validate(ctx context.Context, opts ...ValidationOption)
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#oauth-flow-object
 type OAuthFlow struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	AuthorizationURL string    `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"`
 	TokenURL         string    `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"`
diff --git a/openapi3/server.go b/openapi3/server.go
index e438e92..ea77517 100644
--- a/openapi3/server.go
+++ b/openapi3/server.go
@@ -51,7 +51,7 @@ func (servers Servers) MatchURL(parsedURL *url.URL) (*Server, []string, string)
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#server-object
 type Server struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	URL         string                     `json:"url" yaml:"url"` // Required
 	Description string                     `json:"description,omitempty" yaml:"description,omitempty"`
@@ -236,7 +236,7 @@ func (server *Server) Validate(ctx context.Context, opts ...ValidationOption) (e
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#server-variable-object
 type ServerVariable struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Enum        []string `json:"enum,omitempty" yaml:"enum,omitempty"`
 	Default     string   `json:"default,omitempty" yaml:"default,omitempty"`
diff --git a/openapi3/stringmap.go b/openapi3/stringmap.go
index 354a4c8..d1b91ac 100644
--- a/openapi3/stringmap.go
+++ b/openapi3/stringmap.go
@@ -18,11 +18,10 @@ func unmarshalStringMapP[V any](data []byte) (map[string]*V, *Origin, error) {
 		return nil, nil, err
 	}
 
-	origin, err := deepCast[Origin](m[originKey])
+	origin, err := popOrigin(m, originKey)
 	if err != nil {
 		return nil, nil, err
 	}
-	delete(m, originKey)
 
 	result := make(map[string]*V, len(m))
 	for k, v := range m {
@@ -43,11 +42,10 @@ func unmarshalStringMap[V any](data []byte) (map[string]V, *Origin, error) {
 		return nil, nil, err
 	}
 
-	origin, err := deepCast[Origin](m[originKey])
+	origin, err := popOrigin(m, originKey)
 	if err != nil {
 		return nil, nil, err
 	}
-	delete(m, originKey)
 
 	result := make(map[string]V, len(m))
 	for k, v := range m {
@@ -74,3 +72,17 @@ func deepCast[V any](value any) (*V, error) {
 	}
 	return &result, nil
 }
+
+// popOrigin removes the origin from the map and returns it.
+func popOrigin(m map[string]any, key string) (*Origin, error) {
+	if !IncludeOrigin {
+		return nil, nil
+	}
+
+	origin, err := deepCast[Origin](m[key])
+	if err != nil {
+		return nil, err
+	}
+	delete(m, key)
+	return origin, nil
+}
diff --git a/openapi3/tag.go b/openapi3/tag.go
index 5e4086f..841161e 100644
--- a/openapi3/tag.go
+++ b/openapi3/tag.go
@@ -34,7 +34,7 @@ func (tags Tags) Validate(ctx context.Context, opts ...ValidationOption) error {
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#tag-object
 type Tag struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Name         string        `json:"name,omitempty" yaml:"name,omitempty"`
 	Description  string        `json:"description,omitempty" yaml:"description,omitempty"`
diff --git a/openapi3/xml.go b/openapi3/xml.go
index 7acd818..e960249 100644
--- a/openapi3/xml.go
+++ b/openapi3/xml.go
@@ -9,7 +9,7 @@ import (
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#xml-object
 type XML struct {
 	Extensions map[string]any `json:"-" yaml:"-"`
-	Origin     *Origin        `json:"origin,omitempty" yaml:"origin,omitempty"`
+	Origin     *Origin        `json:"__origin__,omitempty" yaml:"__origin__,omitempty"`
 
 	Name      string `json:"name,omitempty" yaml:"name,omitempty"`
 	Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
diff --git a/openapi3filter/req_resp_decoder.go b/openapi3filter/req_resp_decoder.go
index 19e3609..e5fe2a9 100644
--- a/openapi3filter/req_resp_decoder.go
+++ b/openapi3filter/req_resp_decoder.go
@@ -17,7 +17,7 @@ import (
 	"strconv"
 	"strings"
 
-	"gopkg.in/yaml.v3"
+	"github.com/oasdiff/yaml3"
 
 	"github.com/getkin/kin-openapi/openapi3"
 )
@@ -53,7 +53,7 @@ func (e *ParseError) Error() string {
 	if p := e.Path(); len(p) > 0 {
 		var arr []string
 		for _, v := range p {
-			arr = append(arr, fmt.Sprintf("%v", v))
+			arr = append(arr, fmt.Sprint(v))
 		}
 		msg = append(msg, fmt.Sprintf("path %v", strings.Join(arr, ".")))
 	}
@@ -1269,16 +1269,15 @@ func init() {
 	RegisterBodyDecoder("application/vnd.api+json", JSONBodyDecoder)
 	RegisterBodyDecoder("application/octet-stream", FileBodyDecoder)
 	RegisterBodyDecoder("application/problem+json", JSONBodyDecoder)
-	RegisterBodyDecoder("application/x-www-form-urlencoded", urlencodedBodyDecoder)
-	RegisterBodyDecoder("application/x-yaml", yamlBodyDecoder)
-	RegisterBodyDecoder("application/yaml", yamlBodyDecoder)
-	RegisterBodyDecoder("application/zip", zipFileBodyDecoder)
-	RegisterBodyDecoder("multipart/form-data", multipartBodyDecoder)
-	RegisterBodyDecoder("text/csv", csvBodyDecoder)
-	RegisterBodyDecoder("text/plain", plainBodyDecoder)
+	RegisterBodyDecoder("application/x-www-form-urlencoded", UrlencodedBodyDecoder)
+	RegisterBodyDecoder("application/x-yaml", YamlBodyDecoder)
+	RegisterBodyDecoder("application/yaml", YamlBodyDecoder)
+	RegisterBodyDecoder("multipart/form-data", MultipartBodyDecoder)
+	RegisterBodyDecoder("text/csv", CsvBodyDecoder)
+	RegisterBodyDecoder("text/plain", PlainBodyDecoder)
 }
 
-func plainBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) {
+func PlainBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) {
 	data, err := io.ReadAll(body)
 	if err != nil {
 		return nil, &ParseError{Kind: KindInvalidFormat, Cause: err}
@@ -1298,7 +1297,7 @@ func JSONBodyDecoder(body io.Reader, header http.Header, schema *openapi3.Schema
 	return value, nil
 }
 
-func yamlBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) {
+func YamlBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) {
 	var value any
 	if err := yaml.NewDecoder(body).Decode(&value); err != nil {
 		return nil, &ParseError{Kind: KindInvalidFormat, Cause: err}
@@ -1306,7 +1305,7 @@ func yamlBodyDecoder(body io.Reader, header http.Header, schema *openapi3.Schema
 	return value, nil
 }
 
-func urlencodedBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) {
+func UrlencodedBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) {
 	// Validate schema of request body.
 	// By the OpenAPI 3 specification request body's schema must have type "object".
 	// Properties of the schema describes individual parts of request body.
@@ -1340,18 +1339,6 @@ func urlencodedBodyDecoder(body io.Reader, header http.Header, schema *openapi3.
 	obj := make(map[string]any)
 	dec := &urlValuesDecoder{values: values}
 
-	// Decode schema constructs (allOf, anyOf, oneOf)
-	if err := decodeSchemaConstructs(dec, schema.Value.AllOf, obj, encFn); err != nil {
-		return nil, err
-	}
-	if err := decodeSchemaConstructs(dec, schema.Value.AnyOf, obj, encFn); err != nil {
-		return nil, err
-	}
-	if err := decodeSchemaConstructs(dec, schema.Value.OneOf, obj, encFn); err != nil {
-		return nil, err
-	}
-
-	// Decode properties from the main schema
 	if err := decodeSchemaConstructs(dec, []*openapi3.SchemaRef{schema}, obj, encFn); err != nil {
 		return nil, err
 	}
@@ -1363,6 +1350,18 @@ func urlencodedBodyDecoder(body io.Reader, header http.Header, schema *openapi3.
 // This function is for decoding purposes only and not for validation.
 func decodeSchemaConstructs(dec *urlValuesDecoder, schemas []*openapi3.SchemaRef, obj map[string]any, encFn EncodingFn) error {
 	for _, schemaRef := range schemas {
+
+		// Decode schema constructs (allOf, anyOf, oneOf)
+		if err := decodeSchemaConstructs(dec, schemaRef.Value.AllOf, obj, encFn); err != nil {
+			return err
+		}
+		if err := decodeSchemaConstructs(dec, schemaRef.Value.AnyOf, obj, encFn); err != nil {
+			return err
+		}
+		if err := decodeSchemaConstructs(dec, schemaRef.Value.OneOf, obj, encFn); err != nil {
+			return err
+		}
+
 		for name, prop := range schemaRef.Value.Properties {
 			value, _, err := decodeProperty(dec, name, prop, encFn)
 			if err != nil {
@@ -1391,7 +1390,7 @@ func decodeProperty(dec valueDecoder, name string, prop *openapi3.SchemaRef, enc
 	return decodeValue(dec, name, sm, prop, false)
 }
 
-func multipartBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) {
+func MultipartBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) {
 	if !schema.Value.Type.Is("object") {
 		return nil, errors.New("unsupported schema of request body")
 	}
@@ -1519,8 +1518,9 @@ func FileBodyDecoder(body io.Reader, header http.Header, schema *openapi3.Schema
 	return string(data), nil
 }
 
-// zipFileBodyDecoder is a body decoder that decodes a zip file body to a string.
-func zipFileBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) {
+// ZipFileBodyDecoder is a body decoder that decodes a zip file body to a string.
+// Use with caution as this implementation may be susceptible to a zip bomb attack.
+func ZipFileBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) {
 	buff := bytes.NewBuffer([]byte{})
 	size, err := io.Copy(buff, body)
 	if err != nil {
@@ -1569,8 +1569,8 @@ func zipFileBodyDecoder(body io.Reader, header http.Header, schema *openapi3.Sch
 	return string(content), nil
 }
 
-// csvBodyDecoder is a body decoder that decodes a csv body to a string.
-func csvBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) {
+// CsvBodyDecoder is a body decoder that decodes a csv body to a string.
+func CsvBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) {
 	r := csv.NewReader(body)
 
 	var sb strings.Builder
diff --git a/openapi3filter/validate_request.go b/openapi3filter/validate_request.go
index bf4771a..13e2247 100644
--- a/openapi3filter/validate_request.go
+++ b/openapi3filter/validate_request.go
@@ -9,6 +9,7 @@ import (
 	"net/http"
 	"net/url"
 	"sort"
+	"strings"
 
 	"github.com/getkin/kin-openapi/openapi3"
 )
@@ -29,7 +30,7 @@ var ErrInvalidEmptyValue = errors.New("empty value is not allowed")
 //
 // Note: One can tune the behavior of uniqueItems: true verification
 // by registering a custom function with openapi3.RegisterArrayUniqueItemsChecker
-func ValidateRequest(ctx context.Context, input *RequestValidationInput) (err error) {
+func ValidateRequest(ctx context.Context, input *RequestValidationInput) error {
 	var me openapi3.MultiError
 
 	options := input.Options
@@ -49,10 +50,10 @@ func ValidateRequest(ctx context.Context, input *RequestValidationInput) (err er
 		security = &route.Spec.Security
 	}
 	if security != nil {
-		if err = ValidateSecurityRequirements(ctx, input, *security); err != nil && !options.MultiError {
-			return
-		}
-		if err != nil {
+		if err := ValidateSecurityRequirements(ctx, input, *security); err != nil {
+			if !options.MultiError {
+				return err
+			}
 			me = append(me, err)
 		}
 	}
@@ -66,10 +67,10 @@ func ValidateRequest(ctx context.Context, input *RequestValidationInput) (err er
 			}
 		}
 
-		if err = ValidateParameter(ctx, input, parameter); err != nil && !options.MultiError {
-			return
-		}
-		if err != nil {
+		if err := ValidateParameter(ctx, input, parameter); err != nil {
+			if !options.MultiError {
+				return err
+			}
 			me = append(me, err)
 		}
 	}
@@ -79,10 +80,10 @@ func ValidateRequest(ctx context.Context, input *RequestValidationInput) (err er
 		if options.ExcludeRequestQueryParams && parameter.Value.In == openapi3.ParameterInQuery {
 			continue
 		}
-		if err = ValidateParameter(ctx, input, parameter.Value); err != nil && !options.MultiError {
-			return
-		}
-		if err != nil {
+		if err := ValidateParameter(ctx, input, parameter.Value); err != nil {
+			if !options.MultiError {
+				return err
+			}
 			me = append(me, err)
 		}
 	}
@@ -90,10 +91,10 @@ func ValidateRequest(ctx context.Context, input *RequestValidationInput) (err er
 	// RequestBody
 	requestBody := operation.RequestBody
 	if requestBody != nil && !options.ExcludeRequestBody {
-		if err = ValidateRequestBody(ctx, input, requestBody.Value); err != nil && !options.MultiError {
-			return
-		}
-		if err != nil {
+		if err := ValidateRequestBody(ctx, input, requestBody.Value); err != nil {
+			if !options.MultiError {
+				return err
+			}
 			me = append(me, err)
 		}
 	}
@@ -101,25 +102,36 @@ func ValidateRequest(ctx context.Context, input *RequestValidationInput) (err er
 	if len(me) > 0 {
 		return me
 	}
-	return
+	return nil
 }
 
 // appendToQueryValues adds to query parameters each value in the provided slice
 func appendToQueryValues[T any](q url.Values, parameterName string, v []T) {
 	for _, i := range v {
-		q.Add(parameterName, fmt.Sprintf("%v", i))
+		q.Add(parameterName, fmt.Sprint(i))
+	}
+}
+
+func joinValues(values []any, sep string) string {
+	strValues := make([]string, 0, len(values))
+	for _, v := range values {
+		strValues = append(strValues, fmt.Sprint(v))
 	}
+	return strings.Join(strValues, sep)
 }
 
 // populateDefaultQueryParameters populates default values inside query parameters, while ensuring types are respected
-func populateDefaultQueryParameters(q url.Values, parameterName string, value any) {
+func populateDefaultQueryParameters(q url.Values, parameterName string, value any, explode bool) {
 	switch t := value.(type) {
-	case []interface{}:
-		appendToQueryValues(q, parameterName, t)
+	case []any:
+		if explode {
+			appendToQueryValues(q, parameterName, t)
+		} else {
+			q.Add(parameterName, joinValues(t, ","))
+		}
 	default:
-		q.Add(parameterName, fmt.Sprintf("%v", value))
+		q.Add(parameterName, fmt.Sprint(value))
 	}
-
 }
 
 // ValidateParameter validates a parameter's value by JSON schema.
@@ -175,14 +187,15 @@ func ValidateParameter(ctx context.Context, input *RequestValidationInput, param
 				// Next check `parameter.Required && !found` will catch this.
 			case openapi3.ParameterInQuery:
 				q := req.URL.Query()
-				populateDefaultQueryParameters(q, parameter.Name, value)
+				explode := parameter.Explode != nil && *parameter.Explode
+				populateDefaultQueryParameters(q, parameter.Name, value, explode)
 				req.URL.RawQuery = q.Encode()
 			case openapi3.ParameterInHeader:
-				req.Header.Add(parameter.Name, fmt.Sprintf("%v", value))
+				req.Header.Add(parameter.Name, fmt.Sprint(value))
 			case openapi3.ParameterInCookie:
 				req.AddCookie(&http.Cookie{
 					Name:  parameter.Name,
-					Value: fmt.Sprintf("%v", value),
+					Value: fmt.Sprint(value),
 				})
 			}
 		}
@@ -403,6 +416,23 @@ func validateSecurityRequirement(ctx context.Context, input *RequestValidationIn
 		securitySchemes = components.SecuritySchemes
 	}
 
+	// NOTE that because we could have an `AuthenticationFunc` that reads the request body, we need to provide a fresh `io.Reader` to each iteration of the loop. To make this more performant, we can read the request body once into memory (which may be costly) and then create a fresh `io.Reader` for each `AuthenticationFunc`
+	var data []byte
+
+	if input.Request != nil && input.Request.Body != http.NoBody && input.Request.Body != nil {
+		defer input.Request.Body.Close()
+
+		var err error
+		data, err = io.ReadAll(input.Request.Body)
+		if err != nil {
+			return &RequestError{
+				Input:  input,
+				Reason: "reading failed",
+				Err:    err,
+			}
+		}
+	}
+
 	// For each scheme for the requirement
 	for _, name := range names {
 		var securityScheme *openapi3.SecurityScheme
@@ -418,6 +448,26 @@ func validateSecurityRequirement(ctx context.Context, input *RequestValidationIn
 			}
 		}
 		scopes := securityRequirement[name]
+
+		// if there was a request body, then make sure we provide a new copy of the body in the `input`
+		if data != nil {
+			var err error
+			// Put the data back into the input
+			input.Request.Body = nil
+			if input.Request.GetBody != nil {
+				if input.Request.Body, err = input.Request.GetBody(); err != nil {
+					input.Request.Body = nil
+				}
+			}
+			if input.Request.Body == nil {
+				input.Request.ContentLength = int64(len(data))
+				input.Request.GetBody = func() (io.ReadCloser, error) {
+					return io.NopCloser(bytes.NewReader(data)), nil
+				}
+				input.Request.Body, _ = input.Request.GetBody() // no error return
+			}
+		}
+
 		if err := f(ctx, &AuthenticationInput{
 			RequestValidationInput: input,
 			SecuritySchemeName:     name,
@@ -427,5 +477,24 @@ func validateSecurityRequirement(ctx context.Context, input *RequestValidationIn
 			return err
 		}
 	}
+
+	// if there was a request body, then make sure we put it back into the `input`
+	if data != nil {
+		var err error
+		// Put the data back into the input
+		input.Request.Body = nil
+		if input.Request.GetBody != nil {
+			if input.Request.Body, err = input.Request.GetBody(); err != nil {
+				input.Request.Body = nil
+			}
+		}
+		if input.Request.Body == nil {
+			input.Request.ContentLength = int64(len(data))
+			input.Request.GetBody = func() (io.ReadCloser, error) {
+				return io.NopCloser(bytes.NewReader(data)), nil
+			}
+			input.Request.Body, _ = input.Request.GetBody() // no error return
+		}
+	}
 	return nil
 }
diff --git a/openapi3filter/validate_response.go b/openapi3filter/validate_response.go
index 77f127e..a2dcc03 100644
--- a/openapi3filter/validate_response.go
+++ b/openapi3filter/validate_response.go
@@ -94,7 +94,7 @@ func ValidateResponse(ctx context.Context, input *ResponseValidationInput) error
 	}
 
 	content := response.Content
-	if len(content) == 0 || options.ExcludeResponseBody {
+	if len(content) == 0 {
 		// An operation does not contains a validation schema for responses with this status code.
 		return nil
 	}
diff --git a/openapi3filter/validation_error_encoder.go b/openapi3filter/validation_error_encoder.go
index c03fd13..4369ca6 100644
--- a/openapi3filter/validation_error_encoder.go
+++ b/openapi3filter/validation_error_encoder.go
@@ -160,13 +160,13 @@ func convertSchemaError(e *RequestError, innerErr *openapi3.SchemaError) *Valida
 	if innerErr.SchemaField == "enum" {
 		enums := make([]string, 0, len(innerErr.Schema.Enum))
 		for _, enum := range innerErr.Schema.Enum {
-			enums = append(enums, fmt.Sprintf("%v", enum))
+			enums = append(enums, fmt.Sprint(enum))
 		}
 		cErr.Detail = fmt.Sprintf("value %v at %s must be one of: %s",
 			innerErr.Value,
 			toJSONPointer(innerErr.JSONPointer()),
 			strings.Join(enums, ", "))
-		value := fmt.Sprintf("%v", innerErr.Value)
+		value := fmt.Sprint(innerErr.Value)
 		if e.Parameter != nil &&
 			(e.Parameter.Explode == nil || *e.Parameter.Explode) &&
 			(e.Parameter.Style == "" || e.Parameter.Style == "form") &&
diff --git a/openapi3gen/openapi3gen.go b/openapi3gen/openapi3gen.go
index 78e6901..5d9b64a 100644
--- a/openapi3gen/openapi3gen.go
+++ b/openapi3gen/openapi3gen.go
@@ -290,11 +290,10 @@ func (g *Generator) generateWithoutSaving(parents []*theTypeInfo, t reflect.Type
 
 	case reflect.Slice:
 		if t.Elem().Kind() == reflect.Uint8 {
-			if t == rawMessageType {
-				return &openapi3.SchemaRef{Value: schema}, nil
+			if t != rawMessageType {
+				schema.Type = &openapi3.Types{"string"}
+				schema.Format = "byte"
 			}
-			schema.Type = &openapi3.Types{"string"}
-			schema.Format = "byte"
 		} else {
 			schema.Type = &openapi3.Types{"array"}
 			items, err := g.generateSchemaRefFor(parents, t.Elem(), name, tag)
@@ -430,6 +429,11 @@ func (g *Generator) generateWithoutSaving(parents []*theTypeInfo, t reflect.Type
 
 	// For structs we add the schemas to the component schemas
 	if len(parents) > 1 || g.opts.exportComponentSchemas.ExportTopLevelSchema {
+		// If struct is a time.Time instance, separate component shouldn't be generated
+		if t == timeType {
+			return openapi3.NewSchemaRef(t.Name(), schema), nil
+		}
+
 		typeName := g.generateTypeName(t)
 
 		g.componentSchemaRefs[typeName] = struct{}{}


Test patch:
diff --git a/openapi2/openapi2_test.go b/openapi2/openapi2_test.go
index 8a6bf96..d1b3aa5 100644
--- a/openapi2/openapi2_test.go
+++ b/openapi2/openapi2_test.go
@@ -6,7 +6,7 @@ import (
 	"os"
 	"reflect"
 
-	"github.com/invopop/yaml"
+	"github.com/oasdiff/yaml"
 
 	"github.com/getkin/kin-openapi/openapi2"
 )
diff --git a/openapi2conv/issue1049_test.go b/openapi2conv/issue1049_test.go
new file mode 100644
index 0000000..04ddf85
--- /dev/null
+++ b/openapi2conv/issue1049_test.go
@@ -0,0 +1,86 @@
+package openapi2conv
+
+import (
+	"context"
+	"encoding/json"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestIssue1049(t *testing.T) {
+	spec := `
+{
+  "swagger": "2.0",
+  "info": {
+    "description": "Test for additionalProperties",
+    "version": "v1",
+    "title": "API Test"
+  },
+  "host": "Test",
+  "paths": {
+    "/map": {
+      "post": {
+        "summary": "api test summary",
+        "description": "api test description",
+        "operationId": "apiTestUsingPOST",
+        "consumes": ["application/json"],
+        "produces": ["*/*"],
+        "responses": {
+          "200": {
+            "description": "OK",
+            "schema": {
+              "$ref": "#/definitions/ResponseOfMap"
+            }
+          }
+        }
+      }
+    }
+  },
+  "definitions": {
+    "BaseDictResp": {
+      "type": "object",
+      "properties": {
+        "key": {
+          "type": "string",
+          "description": "key of map"
+        },
+        "value": {
+          "type": "string",
+          "description": "value of map"
+        }
+      },
+      "title": "BaseDictResp",
+      "description": "BaseDictResp description"
+    },
+
+    "ResponseOfMap": {
+      "type": "object",
+      "properties": {
+        "data": {
+          "type": "object",
+          "description": "response data",
+          "additionalProperties": {
+            "type": "array",
+            "items": {
+              "$ref": "#/definitions/BaseDictResp"
+            }
+          }
+        }
+      },
+      "title": "ResponseOfMap"
+    }
+  }
+}
+`
+	doc3, err := v2v3JSON([]byte(spec))
+	require.NoError(t, err)
+
+	spec3, err := json.Marshal(doc3)
+	require.NoError(t, err)
+	const expected = `{"components":{"schemas":{"BaseDictResp":{"description":"BaseDictResp description","properties":{"key":{"description":"key of map","type":"string"},"value":{"description":"value of map","type":"string"}},"title":"BaseDictResp","type":"object"},"ResponseOfMap":{"properties":{"data":{"additionalProperties":{"items":{"$ref":"#/components/schemas/BaseDictResp"},"type":"array"},"description":"response data","type":"object"}},"title":"ResponseOfMap","type":"object"}}},"info":{"description":"Test for additionalProperties","title":"API Test","version":"v1"},"openapi":"3.0.3","paths":{"/map":{"post":{"description":"api test description","operationId":"apiTestUsingPOST","responses":{"200":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/ResponseOfMap"}}},"description":"OK"}},"summary":"api test summary"}}},"servers":[{"url":"https://Test/"}]}`
+	require.JSONEq(t, expected, string(spec3))
+
+	err = doc3.Validate(context.Background())
+	require.NoError(t, err)
+}
diff --git a/openapi2conv/issue1069_test.go b/openapi2conv/issue1069_test.go
new file mode 100644
index 0000000..f7bb8bc
--- /dev/null
+++ b/openapi2conv/issue1069_test.go
@@ -0,0 +1,209 @@
+package openapi2conv
+
+import (
+	"context"
+	"testing"
+
+	"github.com/getkin/kin-openapi/openapi2"
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/oasdiff/yaml"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestIssue1069V2ToV3(t *testing.T) {
+	tests := []struct {
+		name     string
+		v2Spec   string
+		validate func(t *testing.T, v3 *openapi3.T)
+	}{
+		{
+			name: "root level externalDocs",
+			v2Spec: `
+swagger: '2.0'
+info:
+  version: 1.0.0
+  title: title
+externalDocs:
+  url: https://example/root
+  description: Root level documentation
+paths:
+  /test:
+    get:
+      responses:
+        '200':
+          description: description
+`,
+			validate: func(t *testing.T, v3 *openapi3.T) {
+				require.NotNil(t, v3.ExternalDocs)
+				assert.Equal(t, "https://example/root", v3.ExternalDocs.URL)
+				assert.Equal(t, "Root level documentation", v3.ExternalDocs.Description)
+			},
+		},
+		{
+			name: "operation level externalDocs",
+			v2Spec: `
+swagger: '2.0'
+info:
+  version: 1.0.0
+  title: title
+paths:
+  /test:
+    get:
+      externalDocs:
+        url: https://example/operation
+        description: Operation level documentation
+      responses:
+        '200':
+          description: description
+`,
+			validate: func(t *testing.T, v3 *openapi3.T) {
+				op := v3.Paths.Value("/test").Get
+				require.NotNil(t, op.ExternalDocs)
+				assert.Equal(t, "https://example/operation", op.ExternalDocs.URL)
+				assert.Equal(t, "Operation level documentation", op.ExternalDocs.Description)
+			},
+		},
+		{
+			name: "schema level externalDocs",
+			v2Spec: `
+swagger: '2.0'
+info:
+  version: 1.0.0
+  title: title
+definitions:
+  TestSchema:
+    type: object
+    externalDocs:
+      url: https://example/schema
+      description: Schema level documentation
+paths:
+  /test:
+    get:
+      responses:
+        '200':
+          description: description
+          schema:
+            $ref: '#/definitions/TestSchema'
+`,
+			validate: func(t *testing.T, v3 *openapi3.T) {
+				schema := v3.Components.Schemas["TestSchema"].Value
+				require.NotNil(t, schema.ExternalDocs)
+				assert.Equal(t, "https://example/schema", schema.ExternalDocs.URL)
+				assert.Equal(t, "Schema level documentation", schema.ExternalDocs.Description)
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			v3, err := v2v3YAML([]byte(tt.v2Spec))
+			require.NoError(t, err)
+			err = v3.Validate(context.Background())
+			require.NoError(t, err)
+			tt.validate(t, v3)
+		})
+	}
+}
+
+func TestIssue1069V3ToV2(t *testing.T) {
+	tests := []struct {
+		name     string
+		v3Spec   string
+		validate func(t *testing.T, v2 *openapi2.T)
+	}{
+		{
+			name: "root level externalDocs",
+			v3Spec: `
+openapi: 3.0.3
+info:
+  version: 1.0.0
+  title: title
+externalDocs:
+  url: https://example/root
+  description: Root level documentation
+components: {}
+paths:
+  /test:
+    get:
+      responses:
+        '200':
+          description: description
+`,
+			validate: func(t *testing.T, v2 *openapi2.T) {
+				require.NotNil(t, v2.ExternalDocs)
+				assert.Equal(t, "https://example/root", v2.ExternalDocs.URL)
+				assert.Equal(t, "Root level documentation", v2.ExternalDocs.Description)
+			},
+		},
+		{
+			name: "operation level externalDocs",
+			v3Spec: `
+openapi: 3.0.3
+info:
+  version: 1.0.0
+  title: title
+components: {}
+paths:
+  /test:
+    get:
+      externalDocs:
+        url: https://example/operation
+        description: Operation level documentation
+      responses:
+        '200':
+          description: description
+`,
+			validate: func(t *testing.T, v2 *openapi2.T) {
+				op := v2.Paths["/test"].Get
+				require.NotNil(t, op.ExternalDocs)
+				assert.Equal(t, "https://example/operation", op.ExternalDocs.URL)
+				assert.Equal(t, "Operation level documentation", op.ExternalDocs.Description)
+			},
+		},
+		{
+			name: "schema level externalDocs",
+			v3Spec: `
+openapi: 3.0.3
+info:
+  version: 1.0.0
+  title: title
+components:
+  schemas:
+    TestSchema:
+      type: object
+      externalDocs:
+        url: https://example/schema
+        description: Schema level documentation
+paths:
+  /test:
+    get:
+      responses:
+        '200':
+          description: description
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/TestSchema'
+`,
+			validate: func(t *testing.T, v2 *openapi2.T) {
+				schema := v2.Definitions["TestSchema"].Value
+				require.NotNil(t, schema.ExternalDocs)
+				assert.Equal(t, "https://example/schema", schema.ExternalDocs.URL)
+				assert.Equal(t, "Schema level documentation", schema.ExternalDocs.Description)
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var doc3 openapi3.T
+			err := yaml.Unmarshal([]byte(tt.v3Spec), &doc3)
+			require.NoError(t, err)
+
+			v2, err := FromV3(&doc3)
+			require.NoError(t, err)
+			tt.validate(t, v2)
+		})
+	}
+}
diff --git a/openapi2conv/issue187_test.go b/openapi2conv/issue187_test.go
index ffd020b..4cec875 100644
--- a/openapi2conv/issue187_test.go
+++ b/openapi2conv/issue187_test.go
@@ -5,7 +5,7 @@ import (
 	"encoding/json"
 	"testing"
 
-	"github.com/invopop/yaml"
+	"github.com/oasdiff/yaml"
 	"github.com/stretchr/testify/require"
 
 	"github.com/getkin/kin-openapi/openapi2"
diff --git a/openapi2conv/issue558_test.go b/openapi2conv/issue558_test.go
index 2067301..b7fb9dc 100644
--- a/openapi2conv/issue558_test.go
+++ b/openapi2conv/issue558_test.go
@@ -3,7 +3,7 @@ package openapi2conv
 import (
 	"testing"
 
-	"github.com/invopop/yaml"
+	"github.com/oasdiff/yaml"
 	"github.com/stretchr/testify/require"
 )
 
diff --git a/openapi2conv/openapi2_conv_test.go b/openapi2conv/openapi2_conv_test.go
index 3177721..659ccb6 100644
--- a/openapi2conv/openapi2_conv_test.go
+++ b/openapi2conv/openapi2_conv_test.go
@@ -65,6 +65,134 @@ func TestConvOpenAPIV2ToV3(t *testing.T) {
 	require.JSONEq(t, exampleV3, string(data))
 }
 
+func TestConvOpenAPIV2ToV3WithAdditionalPropertiesSchemaRef(t *testing.T) {
+	v2 := []byte(`
+{
+    "basePath": "/v2",
+    "host": "test.example.com",
+    "info": {
+        "title": "MyAPI",
+        "version": "0.1"
+    },
+    "paths": {
+        "/foo": {
+            "get": {
+                "operationId": "getFoo",
+                "produces": [
+                    "application/json"
+                ],
+                "responses": {
+                    "200": {
+                        "description": "returns all information",
+                        "schema":{
+                            "type":"object",
+                            "additionalProperties":{
+                               "$ref":"#/definitions/Foo"
+                            }
+                        }
+                    }
+                },
+                "summary": "get foo"
+            }
+        }
+    },
+	"definitions": {
+	    "Foo": {
+		    "type": "object",
+			"properties": {
+			    "a": {
+				    "type": "string"
+				}
+			}
+		}
+	},
+    "schemes": [
+        "http"
+    ],
+    "swagger": "2.0"
+}
+`)
+
+	var doc2 openapi2.T
+	err := json.Unmarshal(v2, &doc2)
+	require.NoError(t, err)
+
+	doc3, err := ToV3(&doc2)
+	require.NoError(t, err)
+	err = doc3.Validate(context.Background())
+	require.NoError(t, err)
+
+	responseSchema := doc3.Paths.Value("/foo").Get.Responses.Value("200").Value.Content.Get("application/json").Schema.Value
+	require.Equal(t, &openapi3.Types{"object"}, responseSchema.Type)
+	require.Equal(t, "#/components/schemas/Foo", responseSchema.AdditionalProperties.Schema.Ref)
+}
+
+func TestConvOpenAPIV2ToV3WithNestedAdditionalPropertiesSchemaRef(t *testing.T) {
+	v2 := []byte(`
+{
+    "basePath": "/v2",
+    "host": "test.example.com",
+    "info": {
+        "title": "MyAPI",
+        "version": "0.1"
+    },
+    "paths": {
+        "/foo": {
+            "get": {
+                "operationId": "getFoo",
+                "produces": [
+                    "application/json"
+                ],
+                "responses": {
+                    "200": {
+                        "description": "returns all information",
+                        "schema":{
+                            "type":"object",
+                            "additionalProperties":{
+                               "type":"object",
+                               "additionalProperties":{
+                                   "$ref":"#/definitions/Foo"
+                               }
+                            }
+                        }
+                    }
+                },
+                "summary": "get foo"
+            }
+        }
+    },
+	"definitions": {
+	    "Foo": {
+		    "type": "object",
+			"properties": {
+			    "a": {
+				    "type": "string"
+				}
+			}
+		}
+	},
+    "schemes": [
+        "http"
+    ],
+    "swagger": "2.0"
+}
+`)
+
+	var doc2 openapi2.T
+	err := json.Unmarshal(v2, &doc2)
+	require.NoError(t, err)
+
+	doc3, err := ToV3(&doc2)
+	require.NoError(t, err)
+	err = doc3.Validate(context.Background())
+	require.NoError(t, err)
+
+	responseSchema := doc3.Paths.Value("/foo").Get.Responses.Value("200").Value.Content.Get("application/json").Schema.Value
+	require.Equal(t, &openapi3.Types{"object"}, responseSchema.Type)
+	require.Equal(t, &openapi3.Types{"object"}, responseSchema.AdditionalProperties.Schema.Value.Type)
+	require.Equal(t, "#/components/schemas/Foo", responseSchema.AdditionalProperties.Schema.Value.AdditionalProperties.Schema.Ref)
+}
+
 const exampleV2 = `
 {
 	"basePath": "/v2",
diff --git a/openapi3/additionalProperties_test.go b/openapi3/additionalProperties_test.go
index 7a9ec74..46cf0db 100644
--- a/openapi3/additionalProperties_test.go
+++ b/openapi3/additionalProperties_test.go
@@ -6,8 +6,8 @@ import (
 	"os"
 	"testing"
 
+	"github.com/oasdiff/yaml3"
 	"github.com/stretchr/testify/require"
-	"gopkg.in/yaml.v3"
 
 	"github.com/getkin/kin-openapi/openapi3"
 )
diff --git a/openapi3/datetime_schema_test.go b/openapi3/datetime_schema_test.go
new file mode 100644
index 0000000..fec6057
--- /dev/null
+++ b/openapi3/datetime_schema_test.go
@@ -0,0 +1,167 @@
+package openapi3
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+var DateSpec = []byte(`
+components:
+  schemas:
+    Server:
+      properties:
+        date:
+          $ref: "#/components/schemas/timestamp"
+        name:
+          type: string
+      type: object
+    timestamp:
+      type: string
+      format: date
+openapi: "3.0.1"
+paths: {}
+info:
+  version: 1.1.1
+  title: title
+`[1:])
+
+var DateTimeSpec = []byte(`
+components:
+  schemas:
+    Server:
+      properties:
+        datetime:
+          $ref: "#/components/schemas/timestamp"
+        name:
+          type: string
+      type: object
+    timestamp:
+      type: string
+      format: date-time
+openapi: "3.0.1"
+paths: {}
+info:
+  version: 1.1.1
+  title: title
+`[1:])
+
+func TestDateZeroMonth(t *testing.T) {
+	loader := NewLoader()
+	doc, err := loader.LoadFromData(DateSpec)
+	require.NoError(t, err)
+
+	err = doc.Validate(loader.Context)
+	require.NoError(t, err)
+
+	err = doc.Components.Schemas["Server"].Value.VisitJSON(map[string]any{
+		"name": "kin-openapi",
+		"date": "2001-00-03",
+	})
+	require.EqualError(t, err, `Error at "/date": string doesn't match the format "date": string doesn't match pattern "`+FormatOfStringDate+`"`)
+}
+
+func TestDateZeroDay(t *testing.T) {
+	loader := NewLoader()
+	doc, err := loader.LoadFromData(DateSpec)
+	require.NoError(t, err)
+
+	err = doc.Validate(loader.Context)
+	require.NoError(t, err)
+
+	err = doc.Components.Schemas["Server"].Value.VisitJSON(map[string]any{
+		"name": "kin-openapi",
+		"date": "2001-02-00",
+	})
+	require.EqualError(t, err, `Error at "/date": string doesn't match the format "date": string doesn't match pattern "`+FormatOfStringDate+`"`)
+}
+
+func TestDateTimeZeroMonth(t *testing.T) {
+	loader := NewLoader()
+	doc, err := loader.LoadFromData(DateTimeSpec)
+	require.NoError(t, err)
+
+	err = doc.Validate(loader.Context)
+	require.NoError(t, err)
+
+	err = doc.Components.Schemas["Server"].Value.VisitJSON(map[string]any{
+		"name":     "kin-openapi",
+		"datetime": "2001-00-03T04:05:06.789Z",
+	})
+	require.EqualError(t, err, `Error at "/datetime": string doesn't match the format "date-time": string doesn't match pattern "`+FormatOfStringDateTime+`"`)
+}
+
+func TestDateTimeZeroDay(t *testing.T) {
+	loader := NewLoader()
+	doc, err := loader.LoadFromData(DateTimeSpec)
+	require.NoError(t, err)
+
+	err = doc.Validate(loader.Context)
+	require.NoError(t, err)
+
+	err = doc.Components.Schemas["Server"].Value.VisitJSON(map[string]any{
+		"name":     "kin-openapi",
+		"datetime": "2001-02-00T04:05:06.789Z",
+	})
+	require.EqualError(t, err, `Error at "/datetime": string doesn't match the format "date-time": string doesn't match pattern "`+FormatOfStringDateTime+`"`)
+}
+
+func TestDateTimeLeapSecond(t *testing.T) {
+	loader := NewLoader()
+	doc, err := loader.LoadFromData(DateTimeSpec)
+	require.NoError(t, err)
+
+	err = doc.Validate(loader.Context)
+	require.NoError(t, err)
+
+	err = doc.Components.Schemas["Server"].Value.VisitJSON(map[string]any{
+		"name":     "kin-openapi",
+		"datetime": "2016-12-31T23:59:60.000Z", // exact time of the most recent leap second
+	})
+	require.NoError(t, err)
+}
+
+func TestDateTimeHourOutOfBounds(t *testing.T) {
+	loader := NewLoader()
+	doc, err := loader.LoadFromData(DateTimeSpec)
+	require.NoError(t, err)
+
+	err = doc.Validate(loader.Context)
+	require.NoError(t, err)
+
+	err = doc.Components.Schemas["Server"].Value.VisitJSON(map[string]any{
+		"name":     "kin-openapi",
+		"datetime": "2016-12-31T24:00:00.000Z",
+	})
+	require.EqualError(t, err, `Error at "/datetime": string doesn't match the format "date-time": string doesn't match pattern "`+FormatOfStringDateTime+`"`)
+}
+
+func TestDateTimeMinuteOutOfBounds(t *testing.T) {
+	loader := NewLoader()
+	doc, err := loader.LoadFromData(DateTimeSpec)
+	require.NoError(t, err)
+
+	err = doc.Validate(loader.Context)
+	require.NoError(t, err)
+
+	err = doc.Components.Schemas["Server"].Value.VisitJSON(map[string]any{
+		"name":     "kin-openapi",
+		"datetime": "2016-12-31T23:60:00.000Z",
+	})
+	require.EqualError(t, err, `Error at "/datetime": string doesn't match the format "date-time": string doesn't match pattern "`+FormatOfStringDateTime+`"`)
+}
+
+func TestDateTimeSecondOutOfBounds(t *testing.T) {
+	loader := NewLoader()
+	doc, err := loader.LoadFromData(DateTimeSpec)
+	require.NoError(t, err)
+
+	err = doc.Validate(loader.Context)
+	require.NoError(t, err)
+
+	err = doc.Components.Schemas["Server"].Value.VisitJSON(map[string]any{
+		"name":     "kin-openapi",
+		"datetime": "2016-12-31T23:59:61.000Z",
+	})
+	require.EqualError(t, err, `Error at "/datetime": string doesn't match the format "date-time": string doesn't match pattern "`+FormatOfStringDateTime+`"`)
+}
diff --git a/openapi3/encoding_test.go b/openapi3/encoding_test.go
index cbf4738..8f256a3 100644
--- a/openapi3/encoding_test.go
+++ b/openapi3/encoding_test.go
@@ -52,7 +52,7 @@ func encoding() *Encoding {
 			},
 		},
 		Style:         "form",
-		Explode:       BoolPtr(true),
+		Explode:       Ptr(true),
 		AllowReserved: true,
 	}
 }
@@ -74,17 +74,17 @@ func TestEncodingSerializationMethod(t *testing.T) {
 		},
 		{
 			name: "encoding with explode",
-			enc:  &Encoding{Explode: BoolPtr(true)},
+			enc:  &Encoding{Explode: Ptr(true)},
 			want: &SerializationMethod{Style: SerializationForm, Explode: true},
 		},
 		{
 			name: "encoding with no explode",
-			enc:  &Encoding{Explode: BoolPtr(false)},
+			enc:  &Encoding{Explode: Ptr(false)},
 			want: &SerializationMethod{Style: SerializationForm, Explode: false},
 		},
 		{
 			name: "encoding with style and explode ",
-			enc:  &Encoding{Style: SerializationSpaceDelimited, Explode: BoolPtr(false)},
+			enc:  &Encoding{Style: SerializationSpaceDelimited, Explode: Ptr(false)},
 			want: &SerializationMethod{Style: SerializationSpaceDelimited, Explode: false},
 		},
 	}
diff --git a/openapi3/issue241_test.go b/openapi3/issue241_test.go
index 0b37b0a..9a643ed 100644
--- a/openapi3/issue241_test.go
+++ b/openapi3/issue241_test.go
@@ -5,8 +5,8 @@ import (
 	"os"
 	"testing"
 
+	"github.com/oasdiff/yaml3"
 	"github.com/stretchr/testify/require"
-	"gopkg.in/yaml.v3"
 
 	"github.com/getkin/kin-openapi/openapi3"
 )
diff --git a/openapi3/issue376_test.go b/openapi3/issue376_test.go
index 01c0a18..362f08d 100644
--- a/openapi3/issue376_test.go
+++ b/openapi3/issue376_test.go
@@ -46,7 +46,7 @@ info:
 func TestExclusiveValuesOfValuesAdditionalProperties(t *testing.T) {
 	schema := &Schema{
 		AdditionalProperties: AdditionalProperties{
-			Has:    BoolPtr(false),
+			Has:    Ptr(false),
 			Schema: NewSchemaRef("", &Schema{}),
 		},
 	}
@@ -55,7 +55,7 @@ func TestExclusiveValuesOfValuesAdditionalProperties(t *testing.T) {
 
 	schema = &Schema{
 		AdditionalProperties: AdditionalProperties{
-			Has: BoolPtr(false),
+			Has: Ptr(false),
 		},
 	}
 	err = schema.Validate(context.Background())
@@ -139,7 +139,7 @@ info:
 					}
 					apaStr := ""
 					if apa != nil {
-						apaStr = fmt.Sprintf("%v", *apa)
+						apaStr = fmt.Sprint(apa)
 					}
 
 					encoded, err := propSchema.MarshalJSON()
diff --git a/openapi3/issue735_test.go b/openapi3/issue735_test.go
index a846caa..eb57ab8 100644
--- a/openapi3/issue735_test.go
+++ b/openapi3/issue735_test.go
@@ -74,7 +74,7 @@ func TestIssue735(t *testing.T) {
 		},
 		{
 			name:   "multiple of",
-			schema: &Schema{MultipleOf: Float64Ptr(5.0)},
+			schema: &Schema{MultipleOf: Ptr(5.0)},
 			value:  42,
 		},
 		{
@@ -142,7 +142,7 @@ func TestIssue735(t *testing.T) {
 		{
 			name: "additional properties false",
 			schema: &Schema{AdditionalProperties: AdditionalProperties{
-				Has: BoolPtr(false),
+				Has: Ptr(false),
 			}},
 			value:            map[string]any{"foo": 42},
 			extraNotContains: []any{42},
@@ -188,40 +188,40 @@ func TestIssue735(t *testing.T) {
 		{
 			name: "one of (matches more then one)",
 			schema: NewOneOfSchema(
-				&Schema{MultipleOf: Float64Ptr(6)},
-				&Schema{MultipleOf: Float64Ptr(7)},
+				&Schema{MultipleOf: Ptr(6.0)},
+				&Schema{MultipleOf: Ptr(7.0)},
 			),
 			value: 42,
 		},
 		{
 			name: "one of (no matches)",
 			schema: NewOneOfSchema(
-				&Schema{MultipleOf: Float64Ptr(5)},
-				&Schema{MultipleOf: Float64Ptr(10)},
+				&Schema{MultipleOf: Ptr(5.0)},
+				&Schema{MultipleOf: Ptr(10.0)},
 			),
 			value: 42,
 		},
 		{
 			name: "any of",
 			schema: NewAnyOfSchema(
-				&Schema{MultipleOf: Float64Ptr(5)},
-				&Schema{MultipleOf: Float64Ptr(10)},
+				&Schema{MultipleOf: Ptr(5.0)},
+				&Schema{MultipleOf: Ptr(10.0)},
 			),
 			value: 42,
 		},
 		{
 			name: "all of (match some)",
 			schema: NewAllOfSchema(
-				&Schema{MultipleOf: Float64Ptr(6)},
-				&Schema{MultipleOf: Float64Ptr(5)},
+				&Schema{MultipleOf: Ptr(6.0)},
+				&Schema{MultipleOf: Ptr(5.0)},
 			),
 			value: 42,
 		},
 		{
 			name: "all of (no match)",
 			schema: NewAllOfSchema(
-				&Schema{MultipleOf: Float64Ptr(10)},
-				&Schema{MultipleOf: Float64Ptr(5)},
+				&Schema{MultipleOf: Ptr(10.0)},
+				&Schema{MultipleOf: Ptr(5.0)},
 			),
 			value: 42,
 		},
diff --git a/openapi3/issue82_test.go b/openapi3/issue82_test.go
index 3bf242b..27b72e2 100644
--- a/openapi3/issue82_test.go
+++ b/openapi3/issue82_test.go
@@ -8,7 +8,7 @@ import (
 )
 
 func TestIssue82(t *testing.T) {
-	payload := map[string]interface{}{
+	payload := map[string]any{
 		"prop1": "val",
 		"prop3": "val",
 	}
diff --git a/openapi3/issue883_test.go b/openapi3/issue883_test.go
index 28d7163..7609e6a 100644
--- a/openapi3/issue883_test.go
+++ b/openapi3/issue883_test.go
@@ -3,9 +3,9 @@ package openapi3_test
 import (
 	"testing"
 
-	invopopYaml "github.com/invopop/yaml"
+	yaml "github.com/oasdiff/yaml"
+	yamlv3 "github.com/oasdiff/yaml3"
 	"github.com/stretchr/testify/require"
-	v3 "gopkg.in/yaml.v3"
 
 	"github.com/getkin/kin-openapi/openapi3"
 )
@@ -38,8 +38,8 @@ paths:
 	require.NoError(t, err)
 	require.NotNil(t, doc.Paths)
 
-	t.Run("Roundtrip invopop/yaml", func(t *testing.T) {
-		justPaths, err := invopopYaml.Marshal(doc.Paths)
+	t.Run("Roundtrip using yaml pkg", func(t *testing.T) {
+		justPaths, err := yaml.Marshal(doc.Paths)
 		require.NoError(t, err)
 		require.NotNil(t, doc.Paths)
 		require.YAMLEq(t, `
@@ -51,13 +51,13 @@ paths:
         description: OK
 `[1:], string(justPaths))
 
-		marshalledYaml, err := invopopYaml.Marshal(doc)
+		marshalledYaml, err := yaml.Marshal(doc)
 		require.NoError(t, err)
 		require.NotNil(t, doc.Paths)
 		require.YAMLEq(t, spec, string(marshalledYaml))
 
 		var newDoc openapi3.T
-		err = invopopYaml.Unmarshal(marshalledYaml, &newDoc)
+		err = yaml.Unmarshal(marshalledYaml, &newDoc)
 		require.NoError(t, err)
 		require.NotNil(t, newDoc.Paths)
 		require.Equal(t, doc, &newDoc)
@@ -76,7 +76,7 @@ paths:
         description: OK
 `[1:], string(justPaths))
 
-		justPaths, err = v3.Marshal(doc.Paths)
+		justPaths, err = yamlv3.Marshal(doc.Paths)
 		require.NoError(t, err)
 		require.NotNil(t, doc.Paths)
 		require.YAMLEq(t, `
@@ -88,14 +88,14 @@ paths:
         description: OK
 `[1:], string(justPaths))
 
-		marshalledYaml, err := v3.Marshal(doc)
+		marshalledYaml, err := yamlv3.Marshal(doc)
 		require.NoError(t, err)
 		require.NotNil(t, doc.Paths)
 		require.YAMLEq(t, spec, string(marshalledYaml))
 
-		t.Skip("TODO: impl https://pkg.go.dev/gopkg.in/yaml.v3#Unmarshaler on maplike types")
+		t.Skip("TODO: impl https://pkg.go.dev/github.com/oasdiff/yaml3#Unmarshaler on maplike types")
 		var newDoc openapi3.T
-		err = v3.Unmarshal(marshalledYaml, &newDoc)
+		err = yamlv3.Unmarshal(marshalledYaml, &newDoc)
 		require.NoError(t, err)
 		require.NotNil(t, newDoc.Paths)
 		require.Equal(t, doc, &newDoc)
diff --git a/openapi3/issue972_test.go b/openapi3/issue972_test.go
index 3575adc..5aaaae2 100644
--- a/openapi3/issue972_test.go
+++ b/openapi3/issue972_test.go
@@ -3,8 +3,8 @@ package openapi3
 import (
 	"testing"
 
+	"github.com/oasdiff/yaml3"
 	"github.com/stretchr/testify/assert"
-	"gopkg.in/yaml.v3"
 )
 
 func TestIssue972(t *testing.T) {
diff --git a/openapi3/openapi3_test.go b/openapi3/openapi3_test.go
index 3b68c16..bb42df5 100644
--- a/openapi3/openapi3_test.go
+++ b/openapi3/openapi3_test.go
@@ -6,7 +6,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/invopop/yaml"
+	"github.com/oasdiff/yaml"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
diff --git a/openapi3/origin_test.go b/openapi3/origin_test.go
index d955056..2c1d46d 100644
--- a/openapi3/origin_test.go
+++ b/openapi3/origin_test.go
@@ -7,10 +7,17 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
+func unsetIncludeOrigin() {
+	IncludeOrigin = false
+}
+
 func TestOrigin_Info(t *testing.T) {
 	loader := NewLoader()
 	loader.IsExternalRefsAllowed = true
-	loader.IncludeOrigin = true
+
+	IncludeOrigin = true
+	defer unsetIncludeOrigin()
+
 	loader.Context = context.Background()
 
 	doc, err := loader.LoadFromFile("testdata/origin/simple.yaml")
@@ -42,7 +49,10 @@ func TestOrigin_Info(t *testing.T) {
 func TestOrigin_Paths(t *testing.T) {
 	loader := NewLoader()
 	loader.IsExternalRefsAllowed = true
-	loader.IncludeOrigin = true
+
+	IncludeOrigin = true
+	defer unsetIncludeOrigin()
+
 	loader.Context = context.Background()
 
 	doc, err := loader.LoadFromFile("testdata/origin/simple.yaml")
@@ -78,7 +88,10 @@ func TestOrigin_Paths(t *testing.T) {
 func TestOrigin_RequestBody(t *testing.T) {
 	loader := NewLoader()
 	loader.IsExternalRefsAllowed = true
-	loader.IncludeOrigin = true
+
+	IncludeOrigin = true
+	defer unsetIncludeOrigin()
+
 	loader.Context = context.Background()
 
 	doc, err := loader.LoadFromFile("testdata/origin/request_body.yaml")
@@ -105,7 +118,10 @@ func TestOrigin_RequestBody(t *testing.T) {
 func TestOrigin_Responses(t *testing.T) {
 	loader := NewLoader()
 	loader.IsExternalRefsAllowed = true
-	loader.IncludeOrigin = true
+
+	IncludeOrigin = true
+	defer unsetIncludeOrigin()
+
 	loader.Context = context.Background()
 
 	doc, err := loader.LoadFromFile("testdata/origin/simple.yaml")
@@ -140,7 +156,10 @@ func TestOrigin_Responses(t *testing.T) {
 func TestOrigin_Parameters(t *testing.T) {
 	loader := NewLoader()
 	loader.IsExternalRefsAllowed = true
-	loader.IncludeOrigin = true
+
+	IncludeOrigin = true
+	defer unsetIncludeOrigin()
+
 	loader.Context = context.Background()
 
 	doc, err := loader.LoadFromFile("testdata/origin/parameters.yaml")
@@ -173,7 +192,10 @@ func TestOrigin_Parameters(t *testing.T) {
 func TestOrigin_SchemaInAdditionalProperties(t *testing.T) {
 	loader := NewLoader()
 	loader.IsExternalRefsAllowed = true
-	loader.IncludeOrigin = true
+
+	IncludeOrigin = true
+	defer unsetIncludeOrigin()
+
 	loader.Context = context.Background()
 
 	doc, err := loader.LoadFromFile("testdata/origin/additional_properties.yaml")
@@ -201,7 +223,10 @@ func TestOrigin_SchemaInAdditionalProperties(t *testing.T) {
 func TestOrigin_ExternalDocs(t *testing.T) {
 	loader := NewLoader()
 	loader.IsExternalRefsAllowed = true
-	loader.IncludeOrigin = true
+
+	IncludeOrigin = true
+	defer unsetIncludeOrigin()
+
 	loader.Context = context.Background()
 
 	doc, err := loader.LoadFromFile("testdata/origin/external_docs.yaml")
@@ -235,7 +260,10 @@ func TestOrigin_ExternalDocs(t *testing.T) {
 func TestOrigin_Security(t *testing.T) {
 	loader := NewLoader()
 	loader.IsExternalRefsAllowed = true
-	loader.IncludeOrigin = true
+
+	IncludeOrigin = true
+	defer unsetIncludeOrigin()
+
 	loader.Context = context.Background()
 
 	doc, err := loader.LoadFromFile("testdata/origin/security.yaml")
@@ -283,7 +311,10 @@ func TestOrigin_Security(t *testing.T) {
 func TestOrigin_Example(t *testing.T) {
 	loader := NewLoader()
 	loader.IsExternalRefsAllowed = true
-	loader.IncludeOrigin = true
+
+	IncludeOrigin = true
+	defer unsetIncludeOrigin()
+
 	loader.Context = context.Background()
 
 	doc, err := loader.LoadFromFile("testdata/origin/example.yaml")
@@ -319,7 +350,10 @@ func TestOrigin_Example(t *testing.T) {
 func TestOrigin_XML(t *testing.T) {
 	loader := NewLoader()
 	loader.IsExternalRefsAllowed = true
-	loader.IncludeOrigin = true
+
+	IncludeOrigin = true
+	defer unsetIncludeOrigin()
+
 	loader.Context = context.Background()
 
 	doc, err := loader.LoadFromFile("testdata/origin/xml.yaml")
@@ -348,3 +382,37 @@ func TestOrigin_XML(t *testing.T) {
 		},
 		base.Origin.Fields["prefix"])
 }
+
+// TestOrigin_OriginExistsInProperties verifies that loading fails when a specification
+// contains a property named "__origin__", highlighting a limitation in the current implementation.
+func TestOrigin_OriginExistsInProperties(t *testing.T) {
+	var data = `
+paths:
+  /foo:
+    get:
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/Foo"
+components:
+  schemas:
+    Foo:
+      type: object
+      properties:
+        __origin__:
+          type: string
+`
+
+	loader := NewLoader()
+
+	IncludeOrigin = true
+	defer unsetIncludeOrigin()
+
+	_, err := loader.LoadFromData([]byte(data))
+	require.Error(t, err)
+	require.Equal(t, `failed to unmarshal data: json error: invalid character 'p' looking for beginning of value, yaml error: error converting YAML to JSON: yaml: unmarshal errors:
+  line 0: mapping key "__origin__" already defined at line 17`, err.Error())
+}
diff --git a/openapi3/refs_issue247_test.go b/openapi3/refs_issue247_test.go
index 62f056d..ae6d0c3 100644
--- a/openapi3/refs_issue247_test.go
+++ b/openapi3/refs_issue247_test.go
@@ -120,7 +120,7 @@ components:
 	require.NoError(t, err)
 
 	var ptr jsonpointer.Pointer
-	var v interface{}
+	var v any
 	var kind reflect.Kind
 
 	ptr, err = jsonpointer.New("/paths")
diff --git a/openapi3/schema_issue492_test.go b/openapi3/schema_issue492_test.go
index 2fdfcbc..1515672 100644
--- a/openapi3/schema_issue492_test.go
+++ b/openapi3/schema_issue492_test.go
@@ -46,5 +46,5 @@ info:
 		"name": "kin-openapi",
 		"time": "2001-02-03T04:05:06:789Z",
 	})
-	require.ErrorContains(t, err, `Error at "/time": string doesn't match the format "date-time": string doesn't match pattern "^[0-9]{4}-(0[0-9]|10|11|12)-([0-2][0-9]|30|31)T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|(\+|-)[0-9]{2}:[0-9]{2})?$"`)
+	require.EqualError(t, err, `Error at "/time": string doesn't match the format "date-time": string doesn't match pattern "`+FormatOfStringDateTime+`"`)
 }
diff --git a/openapi3/schema_pattern_test.go b/openapi3/schema_pattern_test.go
index fd4c94b..988adfa 100644
--- a/openapi3/schema_pattern_test.go
+++ b/openapi3/schema_pattern_test.go
@@ -17,3 +17,17 @@ func TestPattern(t *testing.T) {
 	require.Equal(t, `^[a-zA-Z\x{0080}-\x{024F}]+$`, intoGoRegexp(`^[a-zA-Z\u0080-\u024F]+$`))
 	require.Equal(t, `^[6789a-zA-Z\x{0080}-\x{024F}]+$`, intoGoRegexp(`^[6789a-zA-Z\u0080-\u024F]+$`))
 }
+
+func TestSchemaPatternCache(t *testing.T) {
+	var schema Schema
+
+	schema.Pattern = `^[a-zA-Z\x{0080}-\x{024F}]+$`
+	cp, err := schema.compilePattern(nil)
+	require.NoError(t, err)
+	require.NotNil(t, cp)
+
+	// The compiled pattern should be cached
+	v, ok := compiledPatterns.Load(schema.Pattern)
+	require.True(t, ok)
+	require.Equal(t, cp, v)
+}
diff --git a/openapi3/schema_test.go b/openapi3/schema_test.go
index d678361..a56148e 100644
--- a/openapi3/schema_test.go
+++ b/openapi3/schema_test.go
@@ -4,13 +4,14 @@ import (
 	"context"
 	"encoding/base64"
 	"encoding/json"
+	"fmt"
 	"math"
 	"reflect"
 	"strings"
 	"testing"
 
+	"github.com/oasdiff/yaml3"
 	"github.com/stretchr/testify/require"
-	"gopkg.in/yaml.v3"
 )
 
 type schemaExample struct {
@@ -542,7 +543,7 @@ var schemaExamples = []schemaExample{
 		Schema: &Schema{
 			Type:        &Types{"array"},
 			MinItems:    2,
-			MaxItems:    Uint64Ptr(3),
+			MaxItems:    Ptr[uint64](3),
 			UniqueItems: true,
 			Items:       NewFloat64Schema().NewRef(),
 		},
@@ -872,7 +873,7 @@ var schemaExamples = []schemaExample{
 		Title: "OBJECT",
 		Schema: &Schema{
 			Type:     &Types{"object"},
-			MaxProps: Uint64Ptr(2),
+			MaxProps: Ptr[uint64](2),
 			Properties: Schemas{
 				"numberProperty": NewFloat64Schema().NewRef(),
 			},
@@ -944,7 +945,7 @@ var schemaExamples = []schemaExample{
 	{
 		Schema: &Schema{
 			Type:                 &Types{"object"},
-			AdditionalProperties: AdditionalProperties{Has: BoolPtr(true)},
+			AdditionalProperties: AdditionalProperties{Has: Ptr(true)},
 		},
 		Serialization: map[string]any{
 			"type":                 "object",
@@ -1490,3 +1491,41 @@ func TestIssue751(t *testing.T) {
 	require.NoError(t, schema.VisitJSON(validData))
 	require.ErrorContains(t, schema.VisitJSON(invalidData), "duplicate items found")
 }
+
+func TestIssue817(t *testing.T) {
+	max := 999999999.99
+	min := -999999999.99
+	mulOf := 0.01
+	schema := &Schema{
+		Type:       &Types{"number"},
+		Max:        &max,
+		Min:        &min,
+		MultipleOf: &mulOf,
+	}
+	validData := []float64{2.07, 8.1, 19628.87, 323.39, 40428.2, 1.13}
+	for _, data := range validData {
+		require.NoError(t, schema.VisitJSON(data))
+	}
+
+	invalidData := []struct {
+		mulfOf float64
+		data   float64
+	}{
+		{
+			mulfOf: 0.01,
+			data:   0.005,
+		},
+		{
+			mulfOf: 3.0,
+			data:   5.0,
+		},
+		{
+			mulfOf: 5.0,
+			data:   2.0,
+		},
+	}
+	for _, data := range invalidData {
+		schema.MultipleOf = &data.mulfOf
+		require.ErrorContains(t, schema.VisitJSON(data.data), fmt.Sprintf("number must be a multiple of %+v", data.mulfOf))
+	}
+}
diff --git a/openapi3filter/issue1045_test.go b/openapi3filter/issue1045_test.go
new file mode 100644
index 0000000..048dbd3
--- /dev/null
+++ b/openapi3filter/issue1045_test.go
@@ -0,0 +1,142 @@
+package openapi3filter
+
+import (
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/getkin/kin-openapi/routers/gorillamux"
+)
+
+func TestIssue1045(t *testing.T) {
+	spec := `
+openapi: 3.0.3
+info:
+  version: 1.0.0
+  title: sample api
+  description: api service paths to test the issue
+paths:
+  /api/path:
+    post:
+      summary: path
+      tags:
+        - api
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: '#/components/schemas/PathRequest' }
+          application/x-www-form-urlencoded:
+            schema: { $ref: '#/components/schemas/PathRequest' }
+      responses:
+        '200':
+          description: Ok
+          content:
+            application/json:
+              schema: { $ref: '#/components/schemas/PathResponse' }
+components:
+  schemas:
+    Msg_Opt:
+      properties:
+        msg: { type: string }
+    Msg:
+      allOf:
+        - $ref: '#/components/schemas/Msg_Opt'
+        - required: [ msg ]
+    Name:
+      properties:
+        name: { type: string }
+      required: [ name ]
+    Id:
+      properties:
+        id:
+          type: string
+          format: uint64
+      required: [ id ]
+    PathRequest:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Msg'
+        - $ref: '#/components/schemas/Name'
+    PathResponse:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/PathRequest'
+        - $ref: '#/components/schemas/Id'
+    `[1:]
+
+	loader := openapi3.NewLoader()
+
+	doc, err := loader.LoadFromData([]byte(spec))
+	require.NoError(t, err)
+
+	err = doc.Validate(loader.Context)
+	require.NoError(t, err)
+
+	router, err := gorillamux.NewRouter(doc)
+	require.NoError(t, err)
+
+	for _, testcase := range []struct {
+		name       string
+		endpoint   string
+		ct         string
+		data       string
+		shouldFail bool
+	}{
+		{
+			name:       "json success",
+			endpoint:   "/api/path",
+			ct:         "application/json",
+			data:       `{"msg":"message", "name":"some+name"}`,
+			shouldFail: false,
+		},
+		{
+			name:       "json failure",
+			endpoint:   "/api/path",
+			ct:         "application/json",
+			data:       `{"name":"some+name"}`,
+			shouldFail: true,
+		},
+
+		// application/x-www-form-urlencoded
+		{
+			name:       "form success",
+			endpoint:   "/api/path",
+			ct:         "application/x-www-form-urlencoded",
+			data:       "msg=message&name=some+name",
+			shouldFail: false,
+		},
+		{
+			name:       "form failure",
+			endpoint:   "/api/path",
+			ct:         "application/x-www-form-urlencoded",
+			data:       "name=some+name",
+			shouldFail: true,
+		},
+	} {
+		t.Run(testcase.name, func(t *testing.T) {
+			data := strings.NewReader(testcase.data)
+			req, err := http.NewRequest("POST", testcase.endpoint, data)
+			require.NoError(t, err)
+			req.Header.Add("Content-Type", testcase.ct)
+
+			route, pathParams, err := router.FindRoute(req)
+			require.NoError(t, err)
+
+			validationInput := &RequestValidationInput{
+				Request:    req,
+				PathParams: pathParams,
+				Route:      route,
+			}
+			err = ValidateRequest(loader.Context, validationInput)
+			if testcase.shouldFail {
+				require.Error(t, err, "This test case should fail "+testcase.data)
+			} else {
+				require.NoError(t, err, "This test case should pass "+testcase.data)
+			}
+		})
+	}
+}
diff --git a/openapi3filter/issue743_test.go b/openapi3filter/issue743_test.go
new file mode 100644
index 0000000..24122e1
--- /dev/null
+++ b/openapi3filter/issue743_test.go
@@ -0,0 +1,207 @@
+package openapi3filter_test
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"testing"
+
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/getkin/kin-openapi/openapi3filter"
+	"github.com/getkin/kin-openapi/routers/gorillamux"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestValidateRequestWithAnAuthenticatorFunc_CanConsumeTheRequestBody(t *testing.T) {
+	const spec = `
+openapi: 3.0.0
+info:
+  title: 'Validator'
+  version: 0.0.1
+paths:
+  /test:
+    post:
+      requestBody:
+        required: true
+        content:
+          text/plain:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: Created
+components:
+  securitySchemes:
+    BearerAuth:
+      type: http
+      scheme: bearer
+      bearerFormat: JWT
+security:
+  - BearerAuth: [ ]
+`
+
+	loader := openapi3.NewLoader()
+	doc, err := loader.LoadFromData([]byte(spec))
+	require.NoError(t, err)
+
+	err = doc.Validate(loader.Context)
+	require.NoError(t, err)
+
+	router, err := gorillamux.NewRouter(doc)
+	require.NoError(t, err)
+
+	req, err := http.NewRequest(http.MethodPost, "/test", strings.NewReader("something"))
+	require.NoError(t, err)
+
+	req.Header.Set("Content-Type", "text/plain")
+
+	route, pathParams, err := router.FindRoute(req)
+	require.NoError(t, err)
+
+	err = openapi3filter.ValidateRequest(
+		context.Background(),
+		&openapi3filter.RequestValidationInput{
+			Request:    req,
+			PathParams: pathParams,
+			Route:      route,
+			Options: &openapi3filter.Options{
+				AuthenticationFunc: func(ctx context.Context, ai *openapi3filter.AuthenticationInput) error {
+					defer req.Body.Close()
+
+					// NOTE that reading from `req.Body` appears to trigger the underlying issue raised in https://github.com/getkin/kin-openapi/issues/743
+					// this doesn't seem to occur when using `req.GetBody()`, but as that's less common to do, we should support both types
+					body, err := io.ReadAll(ai.RequestValidationInput.Request.Body)
+					assert.NoError(t, err)
+
+					// and make sure the parsed body was correct
+					assert.Equal(t, []byte("something"), body)
+
+					return nil
+				},
+			},
+		},
+	)
+
+	require.NoError(t, err)
+}
+
+func TestValidateRequestWithAnAuthenticatorFunc_CanConsumeTheRequestBodyAndThenBeParsedByARouter(t *testing.T) {
+	const spec = `
+openapi: 3.0.0
+info:
+  title: 'Validator'
+  version: 0.0.1
+paths:
+  /test:
+    post:
+      requestBody:
+        required: true
+        content:
+          text/plain:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: Created
+components:
+  securitySchemes:
+    BearerAuth:
+      type: http
+      scheme: bearer
+      bearerFormat: JWT
+security:
+  - BearerAuth: [ ]
+`
+
+	loader := openapi3.NewLoader()
+	doc, err := loader.LoadFromData([]byte(spec))
+	require.NoError(t, err)
+
+	err = doc.Validate(loader.Context)
+	require.NoError(t, err)
+
+	router, err := gorillamux.NewRouter(doc)
+	require.NoError(t, err)
+
+	called := false
+	authenticatorFunc := func(ctx context.Context, ai *openapi3filter.AuthenticationInput) error {
+		defer ai.RequestValidationInput.Request.Body.Close()
+
+		// NOTE that reading from `req.Body` appears to trigger the underlying issue raised in https://github.com/getkin/kin-openapi/issues/743
+		// this doesn't seem to occur when using `req.GetBody()`, but as that's less common to do, we should support both types
+		body, err := io.ReadAll(ai.RequestValidationInput.Request.Body)
+		assert.NoError(t, err)
+
+		// and make sure the parsed body was correct
+		assert.Equal(t, []byte("something"), body)
+
+		return nil
+	}
+
+	use := func(r *http.ServeMux, middlewares ...func(next http.Handler) http.Handler) http.Handler {
+		var s http.Handler
+		s = r
+
+		for _, mw := range middlewares {
+			s = mw(s)
+		}
+
+		return s
+	}
+
+	validationMiddleware := func(next http.Handler) http.Handler {
+		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			route, pathParams, err := router.FindRoute(r)
+			if err != nil {
+				fmt.Println(err.Error())
+				w.WriteHeader(http.StatusInternalServerError)
+				return
+			}
+
+			err = openapi3filter.ValidateRequest(r.Context(), &openapi3filter.RequestValidationInput{
+				Request:    r,
+				PathParams: pathParams,
+				Route:      route,
+				Options: &openapi3filter.Options{
+					AuthenticationFunc: authenticatorFunc,
+				},
+			})
+			require.NoError(t, err)
+
+			next.ServeHTTP(w, r)
+		})
+	}
+
+	r := http.NewServeMux()
+	r.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
+		called = true
+		defer r.Body.Close()
+
+		body, err := io.ReadAll(r.Body)
+		assert.NoError(t, err)
+
+		// and make sure the parsed body was correct
+		assert.Equal(t, []byte("something"), body)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	ts := httptest.NewServer(use(r, validationMiddleware))
+
+	defer ts.Close()
+
+	req, err := http.NewRequest(http.MethodPost, ts.URL+"/test", strings.NewReader("something"))
+	require.NoError(t, err)
+
+	req.Header.Set("Content-Type", "text/plain")
+
+	resp, err := http.DefaultClient.Do(req)
+	require.NoError(t, err)
+
+	assert.Equal(t, http.StatusNoContent, resp.StatusCode)
+	assert.True(t, called, "The POST /test endpoint should have been called, but it wasn't")
+}
diff --git a/openapi3filter/issue991_test.go b/openapi3filter/issue991_test.go
index a81b5b5..10737ea 100644
--- a/openapi3filter/issue991_test.go
+++ b/openapi3filter/issue991_test.go
@@ -9,8 +9,12 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
-func TestValidateRequestDefault(t *testing.T) {
-	const spec = `
+func generateSpec(explode bool) string {
+	explodeStr := "false"
+	if explode {
+		explodeStr = "true"
+	}
+	return `
 openapi: 3.0.0
 info:
   title: 'Validator'
@@ -29,6 +33,7 @@ components:
       in: query
       name: type
       required: false
+      explode: ` + explodeStr + `
       description: Type parameter
       schema:
         type: array
@@ -43,9 +48,9 @@ components:
             - B
             - C
 `
+}
 
-	router := setupTestRouter(t, spec)
-
+func TestValidateRequestDefault(t *testing.T) {
 	type args struct {
 		url      string
 		expected []string
@@ -55,42 +60,57 @@ components:
 		args                 args
 		expectedModification bool
 		expectedErr          error
+		spec                 string
 	}{
 		{
-			name: "Valid request without type parameters set",
+			name: "Valid request without type parameters set and explode is false",
 			args: args{
 				url:      "/category",
-				expected: []string{"A", "B", "C"},
+				expected: []string{"A,B,C"},
 			},
 			expectedModification: false,
 			expectedErr:          nil,
+			spec:                 generateSpec(false),
 		},
 		{
-			name: "Valid request with 1 type parameters set",
+			name: "Valid request with 1 type parameters set and explode is false",
 			args: args{
 				url:      "/category?type=A",
 				expected: []string{"A"},
 			},
 			expectedModification: false,
 			expectedErr:          nil,
+			spec:                 generateSpec(false),
 		},
 		{
-			name: "Valid request with 2 type parameters set",
+			name: "Valid request with 2 type parameters set and explode is false",
 			args: args{
 				url:      "/category?type=A&type=C",
 				expected: []string{"A", "C"},
 			},
 			expectedModification: false,
 			expectedErr:          nil,
+			spec:                 generateSpec(false),
 		},
 		{
-			name: "Valid request with 1 type parameters set out of enum",
+			name: "Valid request with 1 type parameters set out of enum and explode is false",
 			args: args{
 				url:      "/category?type=X",
 				expected: nil,
 			},
 			expectedModification: false,
 			expectedErr:          &RequestError{},
+			spec:                 generateSpec(false),
+		},
+		{
+			name: "Valid request without type parameters set and explode is true",
+			args: args{
+				url:      "/category",
+				expected: []string{"A", "B", "C"},
+			},
+			expectedModification: false,
+			expectedErr:          nil,
+			spec:                 generateSpec(true),
 		},
 	}
 	for _, tc := range tests {
@@ -99,6 +119,7 @@ components:
 			req, err := http.NewRequest(http.MethodGet, tc.args.url, nil)
 			require.NoError(t, err)
 
+			router := setupTestRouter(t, tc.spec)
 			route, pathParams, err := router.FindRoute(req)
 			require.NoError(t, err)
 
diff --git a/openapi3filter/req_resp_decoder_test.go b/openapi3filter/req_resp_decoder_test.go
index 5376390..32c3a45 100644
--- a/openapi3filter/req_resp_decoder_test.go
+++ b/openapi3filter/req_resp_decoder_test.go
@@ -22,8 +22,8 @@ import (
 )
 
 var (
-	explode   = openapi3.BoolPtr(true)
-	noExplode = openapi3.BoolPtr(false)
+	explode   = openapi3.Ptr(true)
+	noExplode = openapi3.Ptr(false)
 	arrayOf   = func(items *openapi3.SchemaRef) *openapi3.SchemaRef {
 		return &openapi3.SchemaRef{Value: &openapi3.Schema{Type: &openapi3.Types{"array"}, Items: items}}
 	}
@@ -1712,7 +1712,7 @@ func TestDecodeBody(t *testing.T) {
 				WithProperty("b", openapi3.NewIntegerSchema()).
 				WithProperty("c", openapi3.NewArraySchema().WithItems(openapi3.NewStringSchema())),
 			encoding: map[string]*openapi3.Encoding{
-				"c": {Style: openapi3.SerializationSpaceDelimited, Explode: openapi3.BoolPtr(false)},
+				"c": {Style: openapi3.SerializationSpaceDelimited, Explode: openapi3.Ptr(false)},
 			},
 			want: map[string]any{"a": "a1", "b": int64(10), "c": []any{"c1", "c2"}},
 		},
@@ -1725,7 +1725,7 @@ func TestDecodeBody(t *testing.T) {
 				WithProperty("b", openapi3.NewIntegerSchema()).
 				WithProperty("c", openapi3.NewArraySchema().WithItems(openapi3.NewStringSchema())),
 			encoding: map[string]*openapi3.Encoding{
-				"c": {Style: openapi3.SerializationPipeDelimited, Explode: openapi3.BoolPtr(false)},
+				"c": {Style: openapi3.SerializationPipeDelimited, Explode: openapi3.Ptr(false)},
 			},
 			want: map[string]any{"a": "a1", "b": int64(10), "c": []any{"c1", "c2"}},
 		},
@@ -1859,7 +1859,7 @@ func TestRegisterAndUnregisterBodyDecoder(t *testing.T) {
 		}
 		return strings.Split(string(data), ","), nil
 	}
-	contentType := "application/csv"
+	const contentType = "application/csv"
 	h := make(http.Header)
 	h.Set(headerCT, contentType)
 
@@ -1867,7 +1867,7 @@ func TestRegisterAndUnregisterBodyDecoder(t *testing.T) {
 	require.Nil(t, originalDecoder)
 
 	RegisterBodyDecoder(contentType, decoder)
-	require.Equal(t, fmt.Sprintf("%v", decoder), fmt.Sprintf("%v", RegisteredBodyDecoder(contentType)))
+	require.Equal(t, fmt.Sprint(decoder), fmt.Sprint(RegisteredBodyDecoder(contentType)))
 
 	body := strings.NewReader("foo,bar")
 	schema := openapi3.NewArraySchema().WithItems(openapi3.NewStringSchema()).NewRef()
diff --git a/openapi3filter/req_resp_encoder_test.go b/openapi3filter/req_resp_encoder_test.go
index 3b7e6a7..7942dab 100644
--- a/openapi3filter/req_resp_encoder_test.go
+++ b/openapi3filter/req_resp_encoder_test.go
@@ -2,7 +2,6 @@ package openapi3filter
 
 import (
 	"fmt"
-	"net/http"
 	"strings"
 	"testing"
 
@@ -14,15 +13,13 @@ func TestRegisterAndUnregisterBodyEncoder(t *testing.T) {
 	encoder = func(body any) (data []byte, err error) {
 		return []byte(strings.Join(body.([]string), ",")), nil
 	}
-	contentType := "text/csv"
-	h := make(http.Header)
-	h.Set(headerCT, contentType)
+	const contentType = "text/csv"
 
 	originalEncoder := RegisteredBodyEncoder(contentType)
 	require.Nil(t, originalEncoder)
 
 	RegisterBodyEncoder(contentType, encoder)
-	require.Equal(t, fmt.Sprintf("%v", encoder), fmt.Sprintf("%v", RegisteredBodyEncoder(contentType)))
+	require.Equal(t, fmt.Sprint(encoder), fmt.Sprint(RegisteredBodyEncoder(contentType)))
 
 	body := []string{"foo", "bar"}
 	got, err := encodeBody(body, contentType)
diff --git a/openapi3filter/validation_error_test.go b/openapi3filter/validation_error_test.go
index 6e2bbd2..e813309 100644
--- a/openapi3filter/validation_error_test.go
+++ b/openapi3filter/validation_error_test.go
@@ -592,13 +592,13 @@ func TestValidationHandler_validateRequest(t *testing.T) {
 					req.Equal(tt.wantErrSchemaReason, innerErr.Reason)
 					pointer := toJSONPointer(innerErr.JSONPointer())
 					req.Equal(tt.wantErrSchemaPath, pointer)
-					req.Equal(fmt.Sprintf("%v", tt.wantErrSchemaValue), fmt.Sprintf("%v", innerErr.Value))
+					req.Equal(fmt.Sprint(tt.wantErrSchemaValue), fmt.Sprint(innerErr.Value))
 
 					if originErr, ok := innerErr.Origin.(*openapi3.SchemaError); ok {
 						req.Equal(tt.wantErrSchemaOriginReason, originErr.Reason)
 						pointer := toJSONPointer(originErr.JSONPointer())
 						req.Equal(tt.wantErrSchemaOriginPath, pointer)
-						req.Equal(fmt.Sprintf("%v", tt.wantErrSchemaOriginValue), fmt.Sprintf("%v", originErr.Value))
+						req.Equal(fmt.Sprint(tt.wantErrSchemaOriginValue), fmt.Sprint(originErr.Value))
 					}
 				} else {
 					req.False(tt.wantErrSchemaReason != "" || tt.wantErrSchemaPath != "",
diff --git a/openapi3filter/zip_file_upload_test.go b/openapi3filter/zip_file_upload_test.go
index 69c6419..b2bf17e 100644
--- a/openapi3filter/zip_file_upload_test.go
+++ b/openapi3filter/zip_file_upload_test.go
@@ -17,6 +17,8 @@ import (
 )
 
 func TestValidateZipFileUpload(t *testing.T) {
+	openapi3filter.RegisterBodyDecoder("application/zip", openapi3filter.ZipFileBodyDecoder)
+
 	const spec = `
 openapi: 3.0.0
 info:
diff --git a/openapi3gen/openapi3gen_test.go b/openapi3gen/openapi3gen_test.go
index 4d9c1e2..e18d861 100644
--- a/openapi3gen/openapi3gen_test.go
+++ b/openapi3gen/openapi3gen_test.go
@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"github.com/stretchr/testify/assert"
 	"reflect"
 	"strconv"
 	"strings"
@@ -407,7 +408,8 @@ func ExampleSchemaCustomizer() {
 			InnerFieldWithTag    int `mymintag:"-1" mymaxtag:"50"`
 			NestedInnerBla
 		}
-		Enum2Field string `json:"enum2" myenumtag:"c,d"`
+		Enum2Field string          `json:"enum2" myenumtag:"c,d"`
+		JsonField  json.RawMessage `json:"rawmsg" myjsontag:"raw"`
 	}
 
 	type Bla struct {
@@ -435,6 +437,9 @@ func ExampleSchemaCustomizer() {
 				schema.Enum = append(schema.Enum, s)
 			}
 		}
+		if tag.Get("myjsontag") != "" {
+			schema.Description = "description"
+		}
 		return nil
 	})
 
@@ -487,6 +492,9 @@ func ExampleSchemaCustomizer() {
 	//         "f"
 	//       ],
 	//       "type": "string"
+	//     },
+	//     "rawmsg": {
+	//       "description": "description"
 	//     }
 	//   },
 	//   "type": "object"
@@ -634,3 +642,28 @@ func ExampleSetSchemar() {
 	//   "type": "object"
 	// }
 }
+
+func TestExportComponentSchemasForTimeProp(t *testing.T) {
+	type Some struct {
+		Name      string
+		CreatedAt time.Time
+	}
+
+	schemas := make(openapi3.Schemas)
+	g := openapi3gen.NewGenerator(
+		openapi3gen.UseAllExportedFields(),
+		openapi3gen.CreateComponentSchemas(openapi3gen.ExportComponentSchemasOptions{
+			ExportComponentSchemas: true,
+		}),
+	)
+
+	ref, err := g.NewSchemaRefForValue(&Some{}, schemas)
+	require.NoError(t, err)
+
+	schema, err := json.MarshalIndent(ref, "", "  ")
+	require.NoError(t, err)
+
+	assert.Condition(t, func() bool {
+		return !strings.Contains(string(schema), "#/components/schemas/Time")
+	}, "Expected no schema for time.Time property but got one: %s", schema)
+}