Repository: getkin/kin-openapi
Pull request #690 (2022-12-01 23:39:19)

fix: setting defaults for oneOf and anyOf
When setting default values for oneOf or anyOf, a temporary value (tempValue) is used to prevent original value from being modified. However, tempValue should to be reset on the next iteration otherwise default value set from previous iteration can modify tempValue which can lead to failure.

The fix is to move the deepcopy inside the loop, which will reset tempValue to the original input.

fenollp: Hi! Please make it more obvious how this patch fixes what it claims.

How does moving the copy inside the loop help?

Can you add minimal a regression test?

Thanks
tomato0111: tempValue should to be reset on the next iteration otherwise default value set from previous iteration can modify the input which can lead to failure.

Thanks!
fenollp: Cool thanks, that makes sense.

Changes to `openapi3filter/validate_set_default_test.go` don't seem to matter for the fix you're providing. Can you explain why you pushed them? 

Please add a regression test.

Thanks

Patch:
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index aa40035..d6950eb 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -36,13 +36,13 @@ jobs:
     - run: echo ${{ steps.go-cache-paths.outputs.go-mod }}
 
     - name: Go Build Cache
-      uses: actions/cache@v2
+      uses: actions/cache@v3
       with:
         path: ${{ steps.go-cache-paths.outputs.go-build }}
         key: ${{ runner.os }}-go-${{ matrix.go }}-build-${{ hashFiles('**/go.sum') }}
 
     - name: Go Mod Cache (go>=1.15)
-      uses: actions/cache@v2
+      uses: actions/cache@v3
       with:
         path: ${{ steps.go-cache-paths.outputs.go-mod }}
         key: ${{ runner.os }}-go-${{ matrix.go }}-mod-${{ hashFiles('**/go.sum') }}
@@ -61,6 +61,7 @@ jobs:
     - run: go fmt ./...
     - run: git --no-pager diff --exit-code
 
+    - run: go test ./...
     - if: runner.os == 'Linux'
       run: go test -count=10 ./...
       env:
@@ -116,7 +117,7 @@ jobs:
           fi
 
           # Ensure impl Validate()
-          if ! git grep -InE 'func [(].+Schema[)] Validate[(]ctx context.Context[)].+error.+[{]'; then
+          if ! git grep -InE 'func [(].+'"$ty"'[)] Validate[(]ctx context.Context, opts [.][.][.]ValidationOption[)].+error.+[{]'; then
             echo "OAI type $ty does not implement Validate()" && exit 1
           fi
 
diff --git a/README.md b/README.md
index c8431f8..c222649 100644
--- a/README.md
+++ b/README.md
@@ -196,6 +196,10 @@ func arrayUniqueItemsChecker(items []interface{}) bool {
 
 ## Sub-v0 breaking API changes
 
+### v0.111.0
+* Changed `func (*_) Validate(ctx context.Context) error` to `func (*_) Validate(ctx context.Context, opts ...ValidationOption) error`.
+* `openapi3.WithValidationOptions(ctx context.Context, opts *ValidationOptions) context.Context` prototype changed to `openapi3.WithValidationOptions(ctx context.Context, opts ...ValidationOption) context.Context`.
+
 ### v0.101.0
 * `openapi3.SchemaFormatValidationDisabled` has been removed in favour of an option `openapi3.EnableSchemaFormatValidation()` passed to `openapi3.T.Validate`. The default behaviour is also now to not validate formats, as the OpenAPI spec mentions the `format` is an open value.
 
diff --git a/openapi3/callback.go b/openapi3/callback.go
index 1e47369..6246d6d 100644
--- a/openapi3/callback.go
+++ b/openapi3/callback.go
@@ -30,7 +30,9 @@ func (c Callbacks) JSONLookup(token string) (interface{}, error) {
 type Callback map[string]*PathItem
 
 // Validate returns an error if Callback does not comply with the OpenAPI spec.
-func (callback Callback) Validate(ctx context.Context) error {
+func (callback Callback) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	keys := make([]string, 0, len(callback))
 	for key := range callback {
 		keys = append(keys, key)
diff --git a/openapi3/components.go b/openapi3/components.go
index 02ae458..16b39f3 100644
--- a/openapi3/components.go
+++ b/openapi3/components.go
@@ -40,7 +40,9 @@ func (components *Components) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if Components does not comply with the OpenAPI spec.
-func (components *Components) Validate(ctx context.Context) (err error) {
+func (components *Components) Validate(ctx context.Context, opts ...ValidationOption) (err error) {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	schemas := make([]string, 0, len(components.Schemas))
 	for name := range components.Schemas {
 		schemas = append(schemas, name)
diff --git a/openapi3/content.go b/openapi3/content.go
index 9443250..8abd411 100644
--- a/openapi3/content.go
+++ b/openapi3/content.go
@@ -106,7 +106,9 @@ func (content Content) Get(mime string) *MediaType {
 }
 
 // Validate returns an error if Content does not comply with the OpenAPI spec.
-func (content Content) Validate(ctx context.Context) error {
+func (content Content) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	keys := make([]string, 0, len(content))
 	for key := range content {
 		keys = append(keys, key)
diff --git a/openapi3/discriminator.go b/openapi3/discriminator.go
index 28a2148..8eb2960 100644
--- a/openapi3/discriminator.go
+++ b/openapi3/discriminator.go
@@ -26,6 +26,8 @@ func (discriminator *Discriminator) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if Discriminator does not comply with the OpenAPI spec.
-func (discriminator *Discriminator) Validate(ctx context.Context) error {
+func (discriminator *Discriminator) Validate(ctx context.Context, opts ...ValidationOption) error {
+	// ctx = WithValidationOptions(ctx, opts...)
+
 	return nil
 }
diff --git a/openapi3/encoding.go b/openapi3/encoding.go
index bc4985c..082d3f2 100644
--- a/openapi3/encoding.go
+++ b/openapi3/encoding.go
@@ -66,7 +66,9 @@ func (encoding *Encoding) SerializationMethod() *SerializationMethod {
 }
 
 // Validate returns an error if Encoding does not comply with the OpenAPI spec.
-func (encoding *Encoding) Validate(ctx context.Context) error {
+func (encoding *Encoding) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if encoding == nil {
 		return nil
 	}
diff --git a/openapi3/example.go b/openapi3/example.go
index 561f09b..e75d4d3 100644
--- a/openapi3/example.go
+++ b/openapi3/example.go
@@ -55,7 +55,9 @@ func (example *Example) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if Example does not comply with the OpenAPI spec.
-func (example *Example) Validate(ctx context.Context) error {
+func (example *Example) Validate(ctx context.Context, opts ...ValidationOption) error {
+	// ctx = WithValidationOptions(ctx, opts...)
+
 	if example.Value != nil && example.ExternalValue != "" {
 		return errors.New("value and externalValue are mutually exclusive")
 	}
diff --git a/openapi3/external_docs.go b/openapi3/external_docs.go
index 75ae0d7..65ec2e8 100644
--- a/openapi3/external_docs.go
+++ b/openapi3/external_docs.go
@@ -29,7 +29,9 @@ func (e *ExternalDocs) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if ExternalDocs does not comply with the OpenAPI spec.
-func (e *ExternalDocs) Validate(ctx context.Context) error {
+func (e *ExternalDocs) Validate(ctx context.Context, opts ...ValidationOption) error {
+	// ctx = WithValidationOptions(ctx, opts...)
+
 	if e.URL == "" {
 		return errors.New("url is required")
 	}
diff --git a/openapi3/header.go b/openapi3/header.go
index c71d3f2..aefaa06 100644
--- a/openapi3/header.go
+++ b/openapi3/header.go
@@ -54,7 +54,9 @@ func (header *Header) SerializationMethod() (*SerializationMethod, error) {
 }
 
 // Validate returns an error if Header does not comply with the OpenAPI spec.
-func (header *Header) Validate(ctx context.Context) error {
+func (header *Header) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if header.Name != "" {
 		return errors.New("header 'name' MUST NOT be specified, it is given in the corresponding headers map")
 	}
diff --git a/openapi3/info.go b/openapi3/info.go
index fa6593c..c67f73a 100644
--- a/openapi3/info.go
+++ b/openapi3/info.go
@@ -31,7 +31,9 @@ func (info *Info) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if Info does not comply with the OpenAPI spec.
-func (info *Info) Validate(ctx context.Context) error {
+func (info *Info) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if contact := info.Contact; contact != nil {
 		if err := contact.Validate(ctx); err != nil {
 			return err
@@ -76,7 +78,9 @@ func (contact *Contact) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if Contact does not comply with the OpenAPI spec.
-func (contact *Contact) Validate(ctx context.Context) error {
+func (contact *Contact) Validate(ctx context.Context, opts ...ValidationOption) error {
+	// ctx = WithValidationOptions(ctx, opts...)
+
 	return nil
 }
 
@@ -100,7 +104,9 @@ func (license *License) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if License does not comply with the OpenAPI spec.
-func (license *License) Validate(ctx context.Context) error {
+func (license *License) Validate(ctx context.Context, opts ...ValidationOption) error {
+	// ctx = WithValidationOptions(ctx, opts...)
+
 	if license.Name == "" {
 		return errors.New("value of license name must be a non-empty string")
 	}
diff --git a/openapi3/link.go b/openapi3/link.go
index 3fb4d78..1040a04 100644
--- a/openapi3/link.go
+++ b/openapi3/link.go
@@ -51,7 +51,9 @@ func (link *Link) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if Link does not comply with the OpenAPI spec.
-func (link *Link) Validate(ctx context.Context) error {
+func (link *Link) Validate(ctx context.Context, opts ...ValidationOption) error {
+	// ctx = WithValidationOptions(ctx, opts...)
+
 	if link.OperationID == "" && link.OperationRef == "" {
 		return errors.New("missing operationId or operationRef on link")
 	}
diff --git a/openapi3/media_type.go b/openapi3/media_type.go
index 1a9bb51..74c11b7 100644
--- a/openapi3/media_type.go
+++ b/openapi3/media_type.go
@@ -75,7 +75,9 @@ func (mediaType *MediaType) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if MediaType does not comply with the OpenAPI spec.
-func (mediaType *MediaType) Validate(ctx context.Context) error {
+func (mediaType *MediaType) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if mediaType == nil {
 		return nil
 	}
diff --git a/openapi3/openapi3.go b/openapi3/openapi3.go
index 510df09..714f280 100644
--- a/openapi3/openapi3.go
+++ b/openapi3/openapi3.go
@@ -56,11 +56,7 @@ func (doc *T) AddServer(server *Server) {
 // Validate returns an error if T does not comply with the OpenAPI spec.
 // Validations Options can be provided to modify the validation behavior.
 func (doc *T) Validate(ctx context.Context, opts ...ValidationOption) error {
-	validationOpts := &ValidationOptions{}
-	for _, opt := range opts {
-		opt(validationOpts)
-	}
-	ctx = WithValidationOptions(ctx, validationOpts)
+	ctx = WithValidationOptions(ctx, opts...)
 
 	if doc.OpenAPI == "" {
 		return errors.New("value of openapi must be a non-empty string")
diff --git a/openapi3/operation.go b/openapi3/operation.go
index 3abc3c4..d877049 100644
--- a/openapi3/operation.go
+++ b/openapi3/operation.go
@@ -127,7 +127,9 @@ func (operation *Operation) AddResponse(status int, response *Response) {
 }
 
 // Validate returns an error if Operation does not comply with the OpenAPI spec.
-func (operation *Operation) Validate(ctx context.Context) error {
+func (operation *Operation) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if v := operation.Parameters; v != nil {
 		if err := v.Validate(ctx); err != nil {
 			return err
diff --git a/openapi3/parameter.go b/openapi3/parameter.go
index dc82a49..9124d92 100644
--- a/openapi3/parameter.go
+++ b/openapi3/parameter.go
@@ -69,7 +69,9 @@ func (parameters Parameters) GetByInAndName(in string, name string) *Parameter {
 }
 
 // Validate returns an error if Parameters does not comply with the OpenAPI spec.
-func (parameters Parameters) Validate(ctx context.Context) error {
+func (parameters Parameters) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	dupes := make(map[string]struct{})
 	for _, parameterRef := range parameters {
 		if v := parameterRef.Value; v != nil {
@@ -247,7 +249,9 @@ func (parameter *Parameter) SerializationMethod() (*SerializationMethod, error)
 }
 
 // Validate returns an error if Parameter does not comply with the OpenAPI spec.
-func (parameter *Parameter) Validate(ctx context.Context) error {
+func (parameter *Parameter) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if parameter.Name == "" {
 		return errors.New("parameter name can't be blank")
 	}
diff --git a/openapi3/path_item.go b/openapi3/path_item.go
index 28ee4b8..5cba0a8 100644
--- a/openapi3/path_item.go
+++ b/openapi3/path_item.go
@@ -123,7 +123,9 @@ func (pathItem *PathItem) SetOperation(method string, operation *Operation) {
 }
 
 // Validate returns an error if PathItem does not comply with the OpenAPI spec.
-func (pathItem *PathItem) Validate(ctx context.Context) error {
+func (pathItem *PathItem) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	operations := pathItem.Operations()
 
 	methods := make([]string, 0, len(operations))
diff --git a/openapi3/paths.go b/openapi3/paths.go
index e3da7d0..2af59f2 100644
--- a/openapi3/paths.go
+++ b/openapi3/paths.go
@@ -12,7 +12,9 @@ import (
 type Paths map[string]*PathItem
 
 // Validate returns an error if Paths does not comply with the OpenAPI spec.
-func (paths Paths) Validate(ctx context.Context) error {
+func (paths Paths) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	normalizedPaths := make(map[string]string, len(paths))
 
 	keys := make([]string, 0, len(paths))
diff --git a/openapi3/refs.go b/openapi3/refs.go
index e85f37e..7311c9d 100644
--- a/openapi3/refs.go
+++ b/openapi3/refs.go
@@ -39,7 +39,8 @@ func (value *CallbackRef) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if CallbackRef does not comply with the OpenAPI spec.
-func (value *CallbackRef) Validate(ctx context.Context) error {
+func (value *CallbackRef) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
 	if v := value.Value; v != nil {
 		return v.Validate(ctx)
 	}
@@ -81,7 +82,8 @@ func (value *ExampleRef) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if ExampleRef does not comply with the OpenAPI spec.
-func (value *ExampleRef) Validate(ctx context.Context) error {
+func (value *ExampleRef) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
 	if v := value.Value; v != nil {
 		return v.Validate(ctx)
 	}
@@ -123,7 +125,8 @@ func (value *HeaderRef) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if HeaderRef does not comply with the OpenAPI spec.
-func (value *HeaderRef) Validate(ctx context.Context) error {
+func (value *HeaderRef) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
 	if v := value.Value; v != nil {
 		return v.Validate(ctx)
 	}
@@ -163,7 +166,8 @@ func (value *LinkRef) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if LinkRef does not comply with the OpenAPI spec.
-func (value *LinkRef) Validate(ctx context.Context) error {
+func (value *LinkRef) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
 	if v := value.Value; v != nil {
 		return v.Validate(ctx)
 	}
@@ -195,7 +199,8 @@ func (value *ParameterRef) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if ParameterRef does not comply with the OpenAPI spec.
-func (value *ParameterRef) Validate(ctx context.Context) error {
+func (value *ParameterRef) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
 	if v := value.Value; v != nil {
 		return v.Validate(ctx)
 	}
@@ -237,7 +242,8 @@ func (value *ResponseRef) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if ResponseRef does not comply with the OpenAPI spec.
-func (value *ResponseRef) Validate(ctx context.Context) error {
+func (value *ResponseRef) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
 	if v := value.Value; v != nil {
 		return v.Validate(ctx)
 	}
@@ -279,7 +285,8 @@ func (value *RequestBodyRef) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if RequestBodyRef does not comply with the OpenAPI spec.
-func (value *RequestBodyRef) Validate(ctx context.Context) error {
+func (value *RequestBodyRef) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
 	if v := value.Value; v != nil {
 		return v.Validate(ctx)
 	}
@@ -328,7 +335,8 @@ func (value *SchemaRef) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if SchemaRef does not comply with the OpenAPI spec.
-func (value *SchemaRef) Validate(ctx context.Context) error {
+func (value *SchemaRef) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
 	if v := value.Value; v != nil {
 		return v.Validate(ctx)
 	}
@@ -370,7 +378,8 @@ func (value *SecuritySchemeRef) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if SecuritySchemeRef does not comply with the OpenAPI spec.
-func (value *SecuritySchemeRef) Validate(ctx context.Context) error {
+func (value *SecuritySchemeRef) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
 	if v := value.Value; v != nil {
 		return v.Validate(ctx)
 	}
diff --git a/openapi3/request_body.go b/openapi3/request_body.go
index 3e7c0d6..225c3d3 100644
--- a/openapi3/request_body.go
+++ b/openapi3/request_body.go
@@ -105,7 +105,9 @@ func (requestBody *RequestBody) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if RequestBody does not comply with the OpenAPI spec.
-func (requestBody *RequestBody) Validate(ctx context.Context) error {
+func (requestBody *RequestBody) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if requestBody.Content == nil {
 		return errors.New("content of the request body is required")
 	}
diff --git a/openapi3/response.go b/openapi3/response.go
index 287e290..d2f907d 100644
--- a/openapi3/response.go
+++ b/openapi3/response.go
@@ -33,7 +33,9 @@ func (responses Responses) Get(status int) *ResponseRef {
 }
 
 // Validate returns an error if Responses does not comply with the OpenAPI spec.
-func (responses Responses) Validate(ctx context.Context) error {
+func (responses Responses) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if len(responses) == 0 {
 		return errors.New("the responses object MUST contain at least one response code")
 	}
@@ -111,7 +113,9 @@ func (response *Response) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if Response does not comply with the OpenAPI spec.
-func (response *Response) Validate(ctx context.Context) error {
+func (response *Response) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if response.Description == nil {
 		return errors.New("a short description of the response is required")
 	}
diff --git a/openapi3/schema.go b/openapi3/schema.go
index d2cd31c..67cc7dc 100644
--- a/openapi3/schema.go
+++ b/openapi3/schema.go
@@ -602,7 +602,8 @@ func (schema *Schema) IsEmpty() bool {
 }
 
 // Validate returns an error if Schema does not comply with the OpenAPI spec.
-func (schema *Schema) Validate(ctx context.Context) error {
+func (schema *Schema) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
 	return schema.validate(ctx, []*Schema{})
 }
 
@@ -933,10 +934,6 @@ func (schema *Schema) visitSetOperations(settings *schemaValidationSettings, val
 			matchedOneOfIdx  = 0
 			tempValue        = value
 		)
-		// make a deep copy to protect origin value from being injected default value that defined in mismatched oneOf schema
-		if settings.asreq || settings.asrep {
-			tempValue = deepcopy.Copy(value)
-		}
 		for idx, item := range v {
 			v := item.Value
 			if v == nil {
@@ -947,6 +944,11 @@ func (schema *Schema) visitSetOperations(settings *schemaValidationSettings, val
 				continue
 			}
 
+			// make a deep copy to protect origin value from being injected default value that defined in mismatched oneOf schema
+			if settings.asreq || settings.asrep {
+				tempValue = deepcopy.Copy(value)
+			}
+
 			if err := v.visitJSON(settings, tempValue); err != nil {
 				validationErrors = append(validationErrors, err)
 				continue
@@ -989,15 +991,15 @@ func (schema *Schema) visitSetOperations(settings *schemaValidationSettings, val
 			matchedAnyOfIdx = 0
 			tempValue       = value
 		)
-		// make a deep copy to protect origin value from being injected default value that defined in mismatched anyOf schema
-		if settings.asreq || settings.asrep {
-			tempValue = deepcopy.Copy(value)
-		}
 		for idx, item := range v {
 			v := item.Value
 			if v == nil {
 				return foundUnresolvedRef(item.Ref)
 			}
+			// make a deep copy to protect origin value from being injected default value that defined in mismatched anyOf schema
+			if settings.asreq || settings.asrep {
+				tempValue = deepcopy.Copy(value)
+			}
 			if err := v.visitJSON(settings, tempValue); err == nil {
 				ok = true
 				matchedAnyOfIdx = idx
diff --git a/openapi3/security_requirements.go b/openapi3/security_requirements.go
index 5929975..dcdad0c 100644
--- a/openapi3/security_requirements.go
+++ b/openapi3/security_requirements.go
@@ -16,7 +16,9 @@ func (srs *SecurityRequirements) With(securityRequirement SecurityRequirement) *
 }
 
 // Validate returns an error if SecurityRequirements does not comply with the OpenAPI spec.
-func (srs SecurityRequirements) Validate(ctx context.Context) error {
+func (srs SecurityRequirements) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	for _, security := range srs {
 		if err := security.Validate(ctx); err != nil {
 			return err
@@ -42,6 +44,8 @@ func (security SecurityRequirement) Authenticate(provider string, scopes ...stri
 }
 
 // Validate returns an error if SecurityRequirement does not comply with the OpenAPI spec.
-func (security *SecurityRequirement) Validate(ctx context.Context) error {
+func (security *SecurityRequirement) Validate(ctx context.Context, opts ...ValidationOption) error {
+	// ctx = WithValidationOptions(ctx, opts...)
+
 	return nil
 }
diff --git a/openapi3/security_scheme.go b/openapi3/security_scheme.go
index 3797389..2b3235d 100644
--- a/openapi3/security_scheme.go
+++ b/openapi3/security_scheme.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"net/url"
 
 	"github.com/go-openapi/jsonpointer"
 
@@ -110,7 +111,9 @@ func (ss *SecurityScheme) WithBearerFormat(value string) *SecurityScheme {
 }
 
 // Validate returns an error if SecurityScheme does not comply with the OpenAPI spec.
-func (ss *SecurityScheme) Validate(ctx context.Context) error {
+func (ss *SecurityScheme) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	hasIn := false
 	hasBearerFormat := false
 	hasFlow := false
@@ -204,20 +207,30 @@ func (flows *OAuthFlows) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if OAuthFlows does not comply with the OpenAPI spec.
-func (flows *OAuthFlows) Validate(ctx context.Context) error {
+func (flows *OAuthFlows) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if v := flows.Implicit; v != nil {
-		return v.Validate(ctx, oAuthFlowTypeImplicit)
+		if err := v.validate(ctx, oAuthFlowTypeImplicit, opts...); err != nil {
+			return fmt.Errorf("the OAuth flow 'implicit' is invalid: %w", err)
+		}
 	}
 	if v := flows.Password; v != nil {
-		return v.Validate(ctx, oAuthFlowTypePassword)
+		if err := v.validate(ctx, oAuthFlowTypePassword, opts...); err != nil {
+			return fmt.Errorf("the OAuth flow 'password' is invalid: %w", err)
+		}
 	}
 	if v := flows.ClientCredentials; v != nil {
-		return v.Validate(ctx, oAuthFlowTypeClientCredentials)
+		if err := v.validate(ctx, oAuthFlowTypeClientCredentials, opts...); err != nil {
+			return fmt.Errorf("the OAuth flow 'clientCredentials' is invalid: %w", err)
+		}
 	}
 	if v := flows.AuthorizationCode; v != nil {
-		return v.Validate(ctx, oAuthFlowAuthorizationCode)
+		if err := v.validate(ctx, oAuthFlowAuthorizationCode, opts...); err != nil {
+			return fmt.Errorf("the OAuth flow 'authorizationCode' is invalid: %w", err)
+		}
 	}
-	return errors.New("no OAuth flow is defined")
+	return nil
 }
 
 // OAuthFlow is specified by OpenAPI/Swagger standard version 3.
@@ -241,20 +254,60 @@ func (flow *OAuthFlow) UnmarshalJSON(data []byte) error {
 	return jsoninfo.UnmarshalStrictStruct(data, flow)
 }
 
-// Validate returns an error if OAuthFlow does not comply with the OpenAPI spec.
-func (flow *OAuthFlow) Validate(ctx context.Context, typ oAuthFlowType) error {
-	if typ == oAuthFlowAuthorizationCode || typ == oAuthFlowTypeImplicit {
-		if v := flow.AuthorizationURL; v == "" {
-			return errors.New("an OAuth flow is missing 'authorizationUrl in authorizationCode or implicit '")
+// Validate returns an error if OAuthFlows does not comply with the OpenAPI spec.
+func (flow *OAuthFlow) Validate(ctx context.Context, opts ...ValidationOption) error {
+	// ctx = WithValidationOptions(ctx, opts...)
+
+	if v := flow.RefreshURL; v != "" {
+		if _, err := url.Parse(v); err != nil {
+			return fmt.Errorf("field 'refreshUrl' is invalid: %w", err)
 		}
 	}
-	if typ != oAuthFlowTypeImplicit {
-		if v := flow.TokenURL; v == "" {
-			return errors.New("an OAuth flow is missing 'tokenUrl in not implicit'")
+
+	if v := flow.Scopes; len(v) == 0 {
+		return errors.New("field 'scopes' is empty or missing")
+	}
+
+	return nil
+}
+
+func (flow *OAuthFlow) validate(ctx context.Context, typ oAuthFlowType, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
+	typeIn := func(types ...oAuthFlowType) bool {
+		for _, ty := range types {
+			if ty == typ {
+				return true
+			}
 		}
+		return false
 	}
-	if v := flow.Scopes; v == nil {
-		return errors.New("an OAuth flow is missing 'scopes'")
+
+	if in := typeIn(oAuthFlowTypeImplicit, oAuthFlowAuthorizationCode); true {
+		switch {
+		case flow.AuthorizationURL == "" && in:
+			return errors.New("field 'authorizationUrl' is empty or missing")
+		case flow.AuthorizationURL != "" && !in:
+			return errors.New("field 'authorizationUrl' should not be set")
+		case flow.AuthorizationURL != "":
+			if _, err := url.Parse(flow.AuthorizationURL); err != nil {
+				return fmt.Errorf("field 'authorizationUrl' is invalid: %w", err)
+			}
+		}
 	}
-	return nil
+
+	if in := typeIn(oAuthFlowTypePassword, oAuthFlowTypeClientCredentials, oAuthFlowAuthorizationCode); true {
+		switch {
+		case flow.TokenURL == "" && in:
+			return errors.New("field 'tokenUrl' is empty or missing")
+		case flow.TokenURL != "" && !in:
+			return errors.New("field 'tokenUrl' should not be set")
+		case flow.TokenURL != "":
+			if _, err := url.Parse(flow.TokenURL); err != nil {
+				return fmt.Errorf("field 'tokenUrl' is invalid: %w", err)
+			}
+		}
+	}
+
+	return flow.Validate(ctx, opts...)
 }
diff --git a/openapi3/server.go b/openapi3/server.go
index 3f989d8..3047996 100644
--- a/openapi3/server.go
+++ b/openapi3/server.go
@@ -16,7 +16,9 @@ import (
 type Servers []*Server
 
 // Validate returns an error if Servers does not comply with the OpenAPI spec.
-func (servers Servers) Validate(ctx context.Context) error {
+func (servers Servers) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	for _, v := range servers {
 		if err := v.Validate(ctx); err != nil {
 			return err
@@ -163,7 +165,9 @@ func (server Server) MatchRawURL(input string) ([]string, string, bool) {
 }
 
 // Validate returns an error if Server does not comply with the OpenAPI spec.
-func (server *Server) Validate(ctx context.Context) (err error) {
+func (server *Server) Validate(ctx context.Context, opts ...ValidationOption) (err error) {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if server.URL == "" {
 		return errors.New("value of url must be a non-empty string")
 	}
@@ -215,7 +219,9 @@ func (serverVariable *ServerVariable) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if ServerVariable does not comply with the OpenAPI spec.
-func (serverVariable *ServerVariable) Validate(ctx context.Context) error {
+func (serverVariable *ServerVariable) Validate(ctx context.Context, opts ...ValidationOption) error {
+	// ctx = WithValidationOptions(ctx, opts...)
+
 	if serverVariable.Default == "" {
 		data, err := serverVariable.MarshalJSON()
 		if err != nil {
diff --git a/openapi3/tag.go b/openapi3/tag.go
index b6c24c8..f151e50 100644
--- a/openapi3/tag.go
+++ b/openapi3/tag.go
@@ -20,7 +20,9 @@ func (tags Tags) Get(name string) *Tag {
 }
 
 // Validate returns an error if Tags does not comply with the OpenAPI spec.
-func (tags Tags) Validate(ctx context.Context) error {
+func (tags Tags) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	for _, v := range tags {
 		if err := v.Validate(ctx); err != nil {
 			return err
@@ -50,7 +52,9 @@ func (t *Tag) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if Tag does not comply with the OpenAPI spec.
-func (t *Tag) Validate(ctx context.Context) error {
+func (t *Tag) Validate(ctx context.Context, opts ...ValidationOption) error {
+	ctx = WithValidationOptions(ctx, opts...)
+
 	if v := t.ExternalDocs; v != nil {
 		if err := v.Validate(ctx); err != nil {
 			return fmt.Errorf("invalid external docs: %w", err)
diff --git a/openapi3/validation_options.go b/openapi3/validation_options.go
index d890087..a74364d 100644
--- a/openapi3/validation_options.go
+++ b/openapi3/validation_options.go
@@ -16,12 +16,29 @@ type ValidationOptions struct {
 type validationOptionsKey struct{}
 
 // EnableSchemaFormatValidation makes Validate not return an error when validating documents that mention schema formats that are not defined by the OpenAPIv3 specification.
+// By default, schema format validation is disabled.
 func EnableSchemaFormatValidation() ValidationOption {
 	return func(options *ValidationOptions) {
 		options.SchemaFormatValidationEnabled = true
 	}
 }
 
+// DisableSchemaFormatValidation does the opposite of EnableSchemaFormatValidation.
+// By default, schema format validation is disabled.
+func DisableSchemaFormatValidation() ValidationOption {
+	return func(options *ValidationOptions) {
+		options.SchemaFormatValidationEnabled = false
+	}
+}
+
+// EnableSchemaPatternValidation does the opposite of DisableSchemaPatternValidation.
+// By default, schema pattern validation is enabled.
+func EnableSchemaPatternValidation() ValidationOption {
+	return func(options *ValidationOptions) {
+		options.SchemaPatternValidationDisabled = false
+	}
+}
+
 // DisableSchemaPatternValidation makes Validate not return an error when validating patterns that are not supported by the Go regexp engine.
 func DisableSchemaPatternValidation() ValidationOption {
 	return func(options *ValidationOptions) {
@@ -29,7 +46,16 @@ func DisableSchemaPatternValidation() ValidationOption {
 	}
 }
 
+// EnableExamplesValidation does the opposite of DisableExamplesValidation.
+// By default, all schema examples are validated.
+func EnableExamplesValidation() ValidationOption {
+	return func(options *ValidationOptions) {
+		options.ExamplesValidationDisabled = false
+	}
+}
+
 // DisableExamplesValidation disables all example schema validation.
+// By default, all schema examples are validated.
 func DisableExamplesValidation() ValidationOption {
 	return func(options *ValidationOptions) {
 		options.ExamplesValidationDisabled = true
@@ -37,7 +63,14 @@ func DisableExamplesValidation() ValidationOption {
 }
 
 // WithValidationOptions allows adding validation options to a context object that can be used when validationg any OpenAPI type.
-func WithValidationOptions(ctx context.Context, options *ValidationOptions) context.Context {
+func WithValidationOptions(ctx context.Context, opts ...ValidationOption) context.Context {
+	if len(opts) == 0 {
+		return ctx
+	}
+	options := &ValidationOptions{}
+	for _, opt := range opts {
+		opt(options)
+	}
 	return context.WithValue(ctx, validationOptionsKey{}, options)
 }
 
diff --git a/openapi3/xml.go b/openapi3/xml.go
index f1ab96b..4ed3d94 100644
--- a/openapi3/xml.go
+++ b/openapi3/xml.go
@@ -29,6 +29,8 @@ func (xml *XML) UnmarshalJSON(data []byte) error {
 }
 
 // Validate returns an error if XML does not comply with the OpenAPI spec.
-func (xml *XML) Validate(ctx context.Context) error {
+func (xml *XML) Validate(ctx context.Context, opts ...ValidationOption) error {
+	// ctx = WithValidationOptions(ctx, opts...)
+
 	return nil // TODO
 }


Test patch:
diff --git a/openapi3/example_validation_test.go b/openapi3/example_validation_test.go
index 79288c2..6ce7c0a 100644
--- a/openapi3/example_validation_test.go
+++ b/openapi3/example_validation_test.go
@@ -221,8 +221,6 @@ func TestExamplesSchemaValidation(t *testing.T) {
 			t.Parallel()
 			for _, tc := range testCases {
 				t.Run(tc.name, func(t *testing.T) {
-					loader := NewLoader()
-
 					spec := bytes.Buffer{}
 					spec.WriteString(`
 openapi: 3.0.3
@@ -339,13 +337,14 @@ components:
 `)
 					spec.WriteString(tc.componentExamples)
 
+					loader := NewLoader()
 					doc, err := loader.LoadFromData(spec.Bytes())
 					require.NoError(t, err)
 
 					if testOption.disableExamplesValidation {
 						err = doc.Validate(loader.Context, DisableExamplesValidation())
 					} else {
-						err = doc.Validate(loader.Context)
+						err = doc.Validate(loader.Context, EnableExamplesValidation())
 					}
 
 					if tc.errContains != "" && !testOption.disableExamplesValidation {
@@ -436,8 +435,6 @@ func TestExampleObjectValidation(t *testing.T) {
 			t.Parallel()
 			for _, tc := range testCases {
 				t.Run(tc.name, func(t *testing.T) {
-					loader := NewLoader()
-
 					spec := bytes.Buffer{}
 					spec.WriteString(`
 openapi: 3.0.3
@@ -506,6 +503,7 @@ components:
 `)
 					spec.WriteString(tc.componentExamples)
 
+					loader := NewLoader()
 					doc, err := loader.LoadFromData(spec.Bytes())
 					require.NoError(t, err)
 
diff --git a/openapi3/schema_test.go b/openapi3/schema_test.go
index abec304..89971cf 100644
--- a/openapi3/schema_test.go
+++ b/openapi3/schema_test.go
@@ -1028,9 +1028,7 @@ func testType(t *testing.T, example schemaTypeExample) func(*testing.T) {
 		}
 		for _, typ := range example.AllInvalid {
 			schema := baseSchema.WithFormat(typ)
-			ctx := WithValidationOptions(context.Background(), &ValidationOptions{
-				SchemaFormatValidationEnabled: true,
-			})
+			ctx := WithValidationOptions(context.Background(), EnableSchemaFormatValidation())
 			err := schema.Validate(ctx)
 			require.Error(t, err)
 		}
diff --git a/openapi3/security_scheme_test.go b/openapi3/security_scheme_test.go
index cba0b84..5958c53 100644
--- a/openapi3/security_scheme_test.go
+++ b/openapi3/security_scheme_test.go
@@ -15,22 +15,18 @@ type securitySchemeExample struct {
 
 func TestSecuritySchemaExample(t *testing.T) {
 	for _, example := range securitySchemeExamples {
-		t.Run(example.title, testSecuritySchemaExample(t, example))
-	}
-}
-
-func testSecuritySchemaExample(t *testing.T, e securitySchemeExample) func(*testing.T) {
-	return func(t *testing.T) {
-		var err error
-		ss := &SecurityScheme{}
-		err = ss.UnmarshalJSON(e.raw)
-		require.NoError(t, err)
-		err = ss.Validate(context.Background())
-		if e.valid {
+		t.Run(example.title, func(t *testing.T) {
+			ss := &SecurityScheme{}
+			err := ss.UnmarshalJSON(example.raw)
 			require.NoError(t, err)
-		} else {
-			require.Error(t, err)
-		}
+
+			err = ss.Validate(context.Background())
+			if example.valid {
+				require.NoError(t, err)
+			} else {
+				require.Error(t, err)
+			}
+		})
 	}
 }
 
@@ -38,72 +34,65 @@ func testSecuritySchemaExample(t *testing.T, e securitySchemeExample) func(*test
 var securitySchemeExamples = []securitySchemeExample{
 	{
 		title: "Basic Authentication Sample",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "http",
   "scheme": "basic"
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "Negotiate Authentication Sample",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "http",
   "scheme": "negotiate"
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "Unknown http Authentication Sample",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "http",
   "scheme": "notvalid"
-}
-`),
+}`),
 		valid: false,
 	},
+
 	{
 		title: "API Key Sample",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "apiKey",
   "name": "api_key",
   "in": "header"
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "apiKey with bearerFormat",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "apiKey",
-	"in": "header",
-	"name": "X-API-KEY",
+  "in": "header",
+  "name": "X-API-KEY",
   "bearerFormat": "Arbitrary text"
-}
-`),
+}`),
 		valid: false,
 	},
+
 	{
 		title: "Bearer Sample with arbitrary format",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "http",
   "scheme": "bearer",
   "bearerFormat": "Arbitrary text"
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "Implicit OAuth2 Sample",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "oauth2",
   "flows": {
     "implicit": {
@@ -114,14 +103,13 @@ var securitySchemeExamples = []securitySchemeExample{
       }
     }
   }
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "OAuth Flow Object Sample",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "oauth2",
   "flows": {
     "implicit": {
@@ -140,14 +128,13 @@ var securitySchemeExamples = []securitySchemeExample{
       }
     }
   }
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "OAuth Flow Object clientCredentials/password",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "oauth2",
   "flows": {
     "clientCredentials": {
@@ -163,79 +150,71 @@ var securitySchemeExamples = []securitySchemeExample{
       }
     }
   }
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "Invalid Basic",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "https",
   "scheme": "basic"
-}
-`),
+}`),
 		valid: false,
 	},
+
 	{
 		title: "Apikey Cookie",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "apiKey",
 	"in": "cookie",
 	"name": "somecookie"
-}
-`),
+}`),
 		valid: true,
 	},
 
 	{
 		title: "OAuth Flow Object with no scopes",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "oauth2",
   "flows": {
     "password": {
       "tokenUrl": "https://example.com/api/oauth/token"
     }
   }
-}
-`),
+}`),
 		valid: false,
 	},
+
 	{
 		title: "OAuth Flow Object with empty scopes",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "oauth2",
   "flows": {
     "password": {
-			"tokenUrl": "https://example.com/api/oauth/token",
-			"scopes": {}
+      "tokenUrl": "https://example.com/api/oauth/token",
+      "scopes": {}
     }
   }
-}
-`),
-		valid: true,
+}`),
+		valid: false,
 	},
+
 	{
 		title: "OIDC Type With URL",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "openIdConnect",
   "openIdConnectUrl": "https://example.com/.well-known/openid-configuration"
-}
-`),
+}`),
 		valid: true,
 	},
+
 	{
 		title: "OIDC Type Without URL",
-		raw: []byte(`
-{
+		raw: []byte(`{
   "type": "openIdConnect",
   "openIdConnectUrl": ""
-}
-`),
+}`),
 		valid: false,
 	},
 }
diff --git a/openapi3filter/validate_set_default_test.go b/openapi3filter/validate_set_default_test.go
index 4550b51..731cbbd 100644
--- a/openapi3filter/validate_set_default_test.go
+++ b/openapi3filter/validate_set_default_test.go
@@ -317,6 +317,70 @@ func TestValidateRequestBodyAndSetDefault(t *testing.T) {
                         }
                       }
                     ]
+                  },
+                  "contact": {
+                    "oneOf": [
+                      {
+                        "type": "object",
+                        "required": ["email"],
+                        "properties": {
+                          "email": {
+                            "type": "string"
+                          },
+                          "allow_image": {
+                            "type": "boolean",
+                            "default": true
+                          }
+                        },
+                        "additionalProperties": false
+                      },
+                      {
+                        "type": "object",
+                        "required": ["phone"],
+                        "properties": {
+                          "phone": {
+                            "type": "string"
+                          },
+                          "allow_text": {
+                            "type": "boolean",
+                            "default": false
+                          }
+                        },
+                        "additionalProperties": false
+                      }
+                    ]
+                  },
+                  "contact2": {
+                    "anyOf": [
+                      {
+                        "type": "object",
+                        "required": ["email"],
+                        "properties": {
+                          "email": {
+                            "type": "string"
+                          },
+                          "allow_image": {
+                            "type": "boolean",
+                            "default": true
+                          }
+                        },
+                        "additionalProperties": false
+                      },
+                      {
+                        "type": "object",
+                        "required": ["phone"],
+                        "properties": {
+                          "phone": {
+                            "type": "string"
+                          },
+                          "allow_text": {
+                            "type": "boolean",
+                            "default": false
+                          }
+                        },
+                        "additionalProperties": false
+                      }
+                    ]
                   }
                 }
               }
@@ -358,6 +422,10 @@ func TestValidateRequestBodyAndSetDefault(t *testing.T) {
 		FBLink   string `json:"fb_link,omitempty"`
 		TWLink   string `json:"tw_link,omitempty"`
 	}
+	type contact struct {
+		Email string `json:"email,omitempty"`
+		Phone string `json:"phone,omitempty"`
+	}
 	type body struct {
 		ID             string         `json:"id,omitempty"`
 		Name           string         `json:"name,omitempty"`
@@ -367,6 +435,8 @@ func TestValidateRequestBodyAndSetDefault(t *testing.T) {
 		Filters        []filter       `json:"filters,omitempty"`
 		SocialNetwork  *socialNetwork `json:"social_network,omitempty"`
 		SocialNetwork2 *socialNetwork `json:"social_network_2,omitempty"`
+		Contact        *contact       `json:"contact,omitempty"`
+		Contact2       *contact       `json:"contact2,omitempty"`
 	}
 
 	testCases := []struct {
@@ -656,6 +726,52 @@ func TestValidateRequestBodyAndSetDefault(t *testing.T) {
     "platform": "facebook",
     "fb_link": "www.facebook.com"
   }
+}
+        `, body)
+			},
+		},
+		{
+			name: "contact(oneOf)",
+			body: body{
+				ID: "bt6kdc3d0cvp6u8u3ft0",
+				Contact: &contact{
+					Phone: "123456",
+				},
+			},
+			bodyAssertion: func(t *testing.T, body string) {
+				require.JSONEq(t, `
+{
+  "id": "bt6kdc3d0cvp6u8u3ft0",
+  "name": "default",
+  "code": 123,
+  "all": false,
+  "contact": {
+    "phone": "123456",
+    "allow_text": false
+  }
+}
+        `, body)
+			},
+		},
+		{
+			name: "contact(anyOf)",
+			body: body{
+				ID: "bt6kdc3d0cvp6u8u3ft0",
+				Contact2: &contact{
+					Phone: "123456",
+				},
+			},
+			bodyAssertion: func(t *testing.T, body string) {
+				require.JSONEq(t, `
+{
+  "id": "bt6kdc3d0cvp6u8u3ft0",
+  "name": "default",
+  "code": 123,
+  "all": false,
+  "contact2": {
+    "phone": "123456",
+    "allow_text": false
+  }
 }
         `, body)
 			},