Repository: getkin/kin-openapi (go)

Issue:
Validation of application/x-yaml request body fails due to unhandled type map[interface{}]interface{} 
**kin-openapi version:** v0.80.0

Hi, I'm trying out the new feature for validating request bodies with content type `application/x-yaml` but `openapi3filter.ValidateRequest` always returns the error: `request body has an error: doesn't match the schema: unhandled value of type map[interface {}]interface {}`.

Am I doing something wrong here (see also full example at the end) or is it a problem in the implementation?

What I saw so far is, that the request body is decoded [here](https://github.com/getkin/kin-openapi/blob/master/openapi3filter/req_resp_decoder.go#L869) as type `map[interface{}]interface{}` using gopkg.in/yaml.v2. Later, during validation against the schema, this leads to the error because [this type switch](https://github.com/getkin/kin-openapi/blob/master/openapi3/schema.go#L790) only expects `map[string]interface{}`, not `map[interface{}]interface{}`.

There is some ongoing discussion in the gopkg.in/yaml.v2 about the map key type, see for example here https://github.com/go-yaml/yaml/issues/384.

<details>
 <summary>Complete Example</summary>

```go
package main

import (
	"bytes"
	"context"
	"github.com/getkin/kin-openapi/openapi3"
	"github.com/getkin/kin-openapi/openapi3filter"
	legacyrouter "github.com/getkin/kin-openapi/routers/legacy"
	"net/http/httptest"
	"testing"
)

func TestOASValidation(t *testing.T) {
	// init router
	loader := openapi3.NewLoader()
	oas, _ := loader.LoadFromData([]byte(`
openapi: '3.0.0'
info:
  title: API
  version: 1.0.0
paths:
  '/path':
    post:
      requestBody:
        required: true
        content:
          application/x-yaml:
            schema:
              type: object
      responses:
        '200':
          description: x
          content:
            application/json:
              schema:
                type: string
`))
	router, _ := legacyrouter.NewRouter(oas)

	// validate request
	r := httptest.NewRequest("POST", "/path", bytes.NewReader([]byte(`
foo: bar
`)))
	r.Header.Set("Content-Type", "application/x-yaml")

	openapi3.SchemaErrorDetailsDisabled = true
	route, pathParams, _ := router.FindRoute(r)
	reqValidationInput := &openapi3filter.RequestValidationInput{
		Request:    r,
		PathParams: pathParams,
		Route:      route,
	}
	if err := openapi3filter.ValidateRequest(context.Background(), reqValidationInput); err != nil {
		// Validation failed with error: request body has an error: doesn't match the schema: unhandled value of type map[interface {}]interface {}
		t.Errorf("Validation failed with error: %v", err)
	}
}
```

</details>



Pull request:
reproduce + fix #444: ValidateRequest for application/x-yaml
Fixes #444 

Patch:
diff --git a/openapi3/schema.go b/openapi3/schema.go
index 443f719..a079698 100644
--- a/openapi3/schema.go
+++ b/openapi3/schema.go
@@ -800,13 +800,22 @@ func (schema *Schema) visitJSON(settings *schemaValidationSettings, value interf
 		return schema.visitJSONArray(settings, value)
 	case map[string]interface{}:
 		return schema.visitJSONObject(settings, value)
-	default:
-		return &SchemaError{
-			Value:       value,
-			Schema:      schema,
-			SchemaField: "type",
-			Reason:      fmt.Sprintf("unhandled value of type %T", value),
+	case map[interface{}]interface{}: // for YAML cf. issue #444
+		values := make(map[string]interface{}, len(value))
+		for key, v := range value {
+			if k, ok := key.(string); ok {
+				values[k] = v
+			}
 		}
+		if len(value) == len(values) {
+			return schema.visitJSONObject(settings, values)
+		}
+	}
+	return &SchemaError{
+		Value:       value,
+		Schema:      schema,
+		SchemaField: "type",
+		Reason:      fmt.Sprintf("unhandled value of type %T", value),
 	}
 }
 


Test patch:
diff --git a/routers/legacy/issue444_test.go b/routers/legacy/issue444_test.go
new file mode 100644
index 0000000..222ecbb
--- /dev/null
+++ b/routers/legacy/issue444_test.go
@@ -0,0 +1,58 @@
+package legacy_test
+
+import (
+	"bytes"
+	"context"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/getkin/kin-openapi/openapi3filter"
+	legacyrouter "github.com/getkin/kin-openapi/routers/legacy"
+	"github.com/stretchr/testify/require"
+)
+
+func TestIssue444(t *testing.T) {
+	loader := openapi3.NewLoader()
+	oas, err := loader.LoadFromData([]byte(`
+openapi: '3.0.0'
+info:
+  title: API
+  version: 1.0.0
+paths:
+  '/path':
+    post:
+      requestBody:
+        required: true
+        content:
+          application/x-yaml:
+            schema:
+              type: object
+      responses:
+        '200':
+          description: x
+          content:
+            application/json:
+              schema:
+                type: string
+`))
+	require.NoError(t, err)
+	router, err := legacyrouter.NewRouter(oas)
+	require.NoError(t, err)
+
+	r := httptest.NewRequest("POST", "/path", bytes.NewReader([]byte(`
+foo: bar
+`)))
+	r.Header.Set("Content-Type", "application/x-yaml")
+
+	openapi3.SchemaErrorDetailsDisabled = true
+	route, pathParams, err := router.FindRoute(r)
+	require.NoError(t, err)
+	reqValidationInput := &openapi3filter.RequestValidationInput{
+		Request:    r,
+		PathParams: pathParams,
+		Route:      route,
+	}
+	err = openapi3filter.ValidateRequest(context.Background(), reqValidationInput)
+	require.NoError(t, err)
+}