Repository: getkin/kin-openapi
Pull request #950 (2024-05-23 22:37:29)

openapi{2,3}: surface both json/yaml unmarshal errors
If an error is encountered when attempting to unmarshal an openapi document formatted as JSON, callers will only see the error message from the YAML unmarshal. This adds an error message that combines both.

fenollp: Do you have a use case where both errors are required?

Or would it be fine to simply return the json error instead of the yml one?
timothympace: If json unmarshal fails (due to invalid openapi schema), the current code will try to unmarshal as yaml. Most of the times this is ok because `yaml.Unmarshal` will surface the same schema issue, but it can also return other errors beforehand like [this one](https://github.com/invopop/yaml/blob/main/yaml.go#L52). In that case, it's very unclear what the actual issue was. This PR may not be the right fix for that use case though.

Patch:
diff --git a/.github/docs/openapi3.txt b/.github/docs/openapi3.txt
index 993d2da..ef878a0 100644
--- a/.github/docs/openapi3.txt
+++ b/.github/docs/openapi3.txt
@@ -170,6 +170,9 @@ func (callback *Callback) Map() (m map[string]*PathItem)
 func (callback *Callback) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Callback.
 
+func (callback *Callback) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Callback.
+
 func (callback *Callback) Set(key string, value *PathItem)
     Set adds or replaces key 'key' of 'callback' with 'value'. Note: 'callback'
     MUST be non-nil
@@ -235,6 +238,9 @@ func NewComponents() Components
 func (components Components) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Components.
 
+func (components Components) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Components.
+
 func (components *Components) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets Components to a copy of data.
 
@@ -255,6 +261,9 @@ type Contact struct {
 func (contact Contact) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Contact.
 
+func (contact Contact) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Contact.
+
 func (contact *Contact) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets Contact to a copy of data.
 
@@ -295,6 +304,9 @@ type Discriminator struct {
 func (discriminator Discriminator) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Discriminator.
 
+func (discriminator Discriminator) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Discriminator.
+
 func (discriminator *Discriminator) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets Discriminator to a copy of data.
 
@@ -319,6 +331,9 @@ func NewEncoding() *Encoding
 func (encoding Encoding) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Encoding.
 
+func (encoding Encoding) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Encoding.
+
 func (encoding *Encoding) SerializationMethod() *SerializationMethod
     SerializationMethod returns a serialization method of request body.
     When serialization method is not defined the method returns the default
@@ -350,6 +365,9 @@ func NewExample(value interface{}) *Example
 func (example Example) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Example.
 
+func (example Example) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Example.
+
 func (example *Example) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets Example to a copy of data.
 
@@ -399,6 +417,9 @@ type ExternalDocs struct {
 func (e ExternalDocs) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of ExternalDocs.
 
+func (e ExternalDocs) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of ExternalDocs.
+
 func (e *ExternalDocs) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets ExternalDocs to a copy of data.
 
@@ -487,6 +508,9 @@ type Info struct {
 func (info Info) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Info.
 
+func (info Info) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Info.
+
 func (info *Info) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets Info to a copy of data.
 
@@ -505,6 +529,9 @@ type License struct {
 func (license License) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of License.
 
+func (license License) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of License.
+
 func (license *License) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets License to a copy of data.
 
@@ -527,6 +554,9 @@ type Link struct {
 func (link Link) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Link.
 
+func (link Link) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Link.
+
 func (link *Link) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets Link to a copy of data.
 
@@ -622,6 +652,9 @@ func (mediaType MediaType) JSONLookup(token string) (interface{}, error)
 func (mediaType MediaType) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of MediaType.
 
+func (mediaType MediaType) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of MediaType.
+
 func (mediaType *MediaType) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets MediaType to a copy of data.
 
@@ -687,6 +720,9 @@ type OAuthFlow struct {
 func (flow OAuthFlow) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of OAuthFlow.
 
+func (flow OAuthFlow) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of OAuthFlow.
+
 func (flow *OAuthFlow) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets OAuthFlow to a copy of data.
 
@@ -708,6 +744,9 @@ type OAuthFlows struct {
 func (flows OAuthFlows) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of OAuthFlows.
 
+func (flows OAuthFlows) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of OAuthFlows.
+
 func (flows *OAuthFlows) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets OAuthFlows to a copy of data.
 
@@ -769,6 +808,9 @@ func (operation Operation) JSONLookup(token string) (interface{}, error)
 func (operation Operation) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Operation.
 
+func (operation Operation) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Operation.
+
 func (operation *Operation) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets Operation to a copy of data.
 
@@ -811,6 +853,9 @@ func (parameter Parameter) JSONLookup(token string) (interface{}, error)
 func (parameter Parameter) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Parameter.
 
+func (parameter Parameter) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Parameter.
+
 func (parameter *Parameter) SerializationMethod() (*SerializationMethod, error)
     SerializationMethod returns a parameter's serialization method. When a
     parameter's serialization method is not defined the method returns the
@@ -901,6 +946,9 @@ func (pathItem *PathItem) GetOperation(method string) *Operation
 func (pathItem PathItem) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of PathItem.
 
+func (pathItem PathItem) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of PathItem.
+
 func (pathItem *PathItem) Operations() map[string]*Operation
 
 func (pathItem *PathItem) SetOperation(method string, operation *Operation)
@@ -963,8 +1011,8 @@ func (paths *Paths) Map() (m map[string]*PathItem)
 func (paths *Paths) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Paths.
 
-func (paths *Paths) MarshalYAML() (any, error)
-    Support YAML Marshaler interface for gopkg.in/yaml
+func (paths *Paths) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Paths.
 
 func (paths *Paths) Set(key string, value *PathItem)
     Set adds or replaces key 'key' of 'paths' with 'value'. Note: 'paths' MUST
@@ -1031,6 +1079,9 @@ func (requestBody *RequestBody) GetMediaType(mediaType string) *MediaType
 func (requestBody RequestBody) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of RequestBody.
 
+func (requestBody RequestBody) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of RequestBody.
+
 func (requestBody *RequestBody) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets RequestBody to a copy of data.
 
@@ -1097,6 +1148,9 @@ func NewResponse() *Response
 func (response Response) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Response.
 
+func (response Response) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Response.
+
 func (response *Response) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets Response to a copy of data.
 
@@ -1177,8 +1231,8 @@ func (responses *Responses) Map() (m map[string]*ResponseRef)
 func (responses *Responses) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Responses.
 
-func (responses *Responses) MarshalYAML() (any, error)
-    Support YAML Marshaler interface for gopkg.in/yaml
+func (responses *Responses) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Responses.
 
 func (responses *Responses) Set(key string, value *ResponseRef)
     Set adds or replaces key 'key' of 'responses' with 'value'. Note:
@@ -1307,6 +1361,9 @@ func (schema Schema) JSONLookup(token string) (interface{}, error)
 func (schema Schema) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Schema.
 
+func (schema Schema) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Schema.
+
 func (schema *Schema) NewRef() *SchemaRef
 
 func (schema *Schema) PermitsNull() bool
@@ -1536,6 +1593,9 @@ func NewSecurityScheme() *SecurityScheme
 func (ss SecurityScheme) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of SecurityScheme.
 
+func (ss SecurityScheme) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of SecurityScheme.
+
 func (ss *SecurityScheme) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets SecurityScheme to a copy of data.
 
@@ -1611,6 +1671,9 @@ func (server *Server) BasePath() (string, error)
 func (server Server) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Server.
 
+func (server Server) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Server.
+
 func (server Server) MatchRawURL(input string) ([]string, string, bool)
 
 func (server Server) ParameterNames() ([]string, error)
@@ -1634,6 +1697,9 @@ type ServerVariable struct {
 func (serverVariable ServerVariable) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of ServerVariable.
 
+func (serverVariable ServerVariable) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of ServerVariable.
+
 func (serverVariable *ServerVariable) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets ServerVariable to a copy of data.
 
@@ -1699,6 +1765,9 @@ func (doc *T) JSONLookup(token string) (interface{}, error)
 func (doc *T) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of T.
 
+func (doc *T) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of T.
+
 func (doc *T) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets T to a copy of data.
 
@@ -1719,6 +1788,9 @@ type Tag struct {
 func (t Tag) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Tag.
 
+func (t Tag) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of Tag.
+
 func (t *Tag) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets Tag to a copy of data.
 
@@ -1813,6 +1885,9 @@ type XML struct {
 func (xml XML) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of XML.
 
+func (xml XML) MarshalYAML() (interface{}, error)
+    MarshalYAML returns the YAML encoding of XML.
+
 func (xml *XML) UnmarshalJSON(data []byte) error
     UnmarshalJSON sets XML to a copy of data.
 
diff --git a/maps.sh b/maps.sh
index 0f7e14c..7e335b0 100755
--- a/maps.sh
+++ b/maps.sh
@@ -156,8 +156,8 @@ EOF
 
 maplike_UnMarsh() {
 	cat <<EOF >>"$maplike"
-// MarshalJSON returns the JSON encoding of ${type#'*'}.
-func (${name} ${type}) MarshalJSON() ([]byte, error) {
+// MarshalYAML returns the YAML encoding of ${type#'*'}.
+func (${name} ${type}) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, ${name}.Len()+len(${name}.Extensions))
 	for k, v := range ${name}.Extensions {
 		m[k] = v
@@ -165,7 +165,16 @@ func (${name} ${type}) MarshalJSON() ([]byte, error) {
 	for k, v := range ${name}.Map() {
 		m[k] = v
 	}
-	return json.Marshal(m)
+	return m, nil
+}
+
+// MarshalJSON returns the JSON encoding of ${type#'*'}.
+func (${name} ${type}) MarshalJSON() ([]byte, error) {
+	${name}Yaml, err := ${name}.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(${name}Yaml)
 }
 
 // UnmarshalJSON sets ${type#'*'} to a copy of data.
diff --git a/openapi2/marsh.go b/openapi2/marsh.go
index b15f3b8..5aa162a 100644
--- a/openapi2/marsh.go
+++ b/openapi2/marsh.go
@@ -17,10 +17,18 @@ func unmarshalError(jsonUnmarshalErr error) error {
 }
 
 func unmarshal(data []byte, v interface{}) error {
+	var jsonErr, yamlErr error
+
 	// See https://github.com/getkin/kin-openapi/issues/680
-	if err := json.Unmarshal(data, v); err != nil {
-		// UnmarshalStrict(data, v) TODO: investigate how ymlv3 handles duplicate map keys
-		return yaml.Unmarshal(data, v)
+	if jsonErr = json.Unmarshal(data, v); jsonErr == nil {
+		return nil
+	}
+
+	// UnmarshalStrict(data, v) TODO: investigate how ymlv3 handles duplicate map keys
+	if yamlErr = yaml.Unmarshal(data, v); yamlErr == nil {
+		return nil
 	}
-	return nil
+
+	// If both unmarshaling attempts fail, return a new error that includes both errors
+	return fmt.Errorf("failed to unmarshal data: json error: %v, yaml error: %v", jsonErr, yamlErr)
 }
diff --git a/openapi3/components.go b/openapi3/components.go
index 656ea19..c466632 100644
--- a/openapi3/components.go
+++ b/openapi3/components.go
@@ -43,6 +43,15 @@ func NewComponents() Components {
 
 // MarshalJSON returns the JSON encoding of Components.
 func (components Components) MarshalJSON() ([]byte, error) {
+	x, err := components.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of Components.
+func (components Components) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 9+len(components.Extensions))
 	for k, v := range components.Extensions {
 		m[k] = v
@@ -74,7 +83,7 @@ func (components Components) MarshalJSON() ([]byte, error) {
 	if x := components.Callbacks; len(x) != 0 {
 		m["callbacks"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets Components to a copy of data.
diff --git a/openapi3/contact.go b/openapi3/contact.go
index e60d281..7b707ce 100644
--- a/openapi3/contact.go
+++ b/openapi3/contact.go
@@ -17,6 +17,15 @@ type Contact struct {
 
 // MarshalJSON returns the JSON encoding of Contact.
 func (contact Contact) MarshalJSON() ([]byte, error) {
+	x, err := contact.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of Contact.
+func (contact Contact) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 3+len(contact.Extensions))
 	for k, v := range contact.Extensions {
 		m[k] = v
@@ -30,7 +39,7 @@ func (contact Contact) MarshalJSON() ([]byte, error) {
 	if x := contact.Email; x != "" {
 		m["email"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets Contact to a copy of data.
diff --git a/openapi3/discriminator.go b/openapi3/discriminator.go
index abb4807..ae36f41 100644
--- a/openapi3/discriminator.go
+++ b/openapi3/discriminator.go
@@ -16,6 +16,15 @@ type Discriminator struct {
 
 // MarshalJSON returns the JSON encoding of Discriminator.
 func (discriminator Discriminator) MarshalJSON() ([]byte, error) {
+	x, err := discriminator.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of Discriminator.
+func (discriminator Discriminator) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 2+len(discriminator.Extensions))
 	for k, v := range discriminator.Extensions {
 		m[k] = v
@@ -24,7 +33,7 @@ func (discriminator Discriminator) MarshalJSON() ([]byte, error) {
 	if x := discriminator.Mapping; len(x) != 0 {
 		m["mapping"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets Discriminator to a copy of data.
diff --git a/openapi3/encoding.go b/openapi3/encoding.go
index 8e81027..57d20ee 100644
--- a/openapi3/encoding.go
+++ b/openapi3/encoding.go
@@ -41,6 +41,15 @@ func (encoding *Encoding) WithHeaderRef(name string, ref *HeaderRef) *Encoding {
 
 // MarshalJSON returns the JSON encoding of Encoding.
 func (encoding Encoding) MarshalJSON() ([]byte, error) {
+	x, err := encoding.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of Encoding.
+func (encoding Encoding) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 5+len(encoding.Extensions))
 	for k, v := range encoding.Extensions {
 		m[k] = v
@@ -60,7 +69,7 @@ func (encoding Encoding) MarshalJSON() ([]byte, error) {
 	if x := encoding.AllowReserved; x {
 		m["allowReserved"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets Encoding to a copy of data.
diff --git a/openapi3/example.go b/openapi3/example.go
index 44e71d8..a1a5a2b 100644
--- a/openapi3/example.go
+++ b/openapi3/example.go
@@ -23,6 +23,15 @@ func NewExample(value interface{}) *Example {
 
 // MarshalJSON returns the JSON encoding of Example.
 func (example Example) MarshalJSON() ([]byte, error) {
+	x, err := example.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of Example.
+func (example Example) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 4+len(example.Extensions))
 	for k, v := range example.Extensions {
 		m[k] = v
@@ -39,7 +48,7 @@ func (example Example) MarshalJSON() ([]byte, error) {
 	if x := example.ExternalValue; x != "" {
 		m["externalValue"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets Example to a copy of data.
diff --git a/openapi3/external_docs.go b/openapi3/external_docs.go
index 7190be4..40e9f3d 100644
--- a/openapi3/external_docs.go
+++ b/openapi3/external_docs.go
@@ -19,6 +19,15 @@ type ExternalDocs struct {
 
 // MarshalJSON returns the JSON encoding of ExternalDocs.
 func (e ExternalDocs) MarshalJSON() ([]byte, error) {
+	x, err := e.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of ExternalDocs.
+func (e ExternalDocs) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 2+len(e.Extensions))
 	for k, v := range e.Extensions {
 		m[k] = v
@@ -29,7 +38,7 @@ func (e ExternalDocs) MarshalJSON() ([]byte, error) {
 	if x := e.URL; x != "" {
 		m["url"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets ExternalDocs to a copy of data.
diff --git a/openapi3/info.go b/openapi3/info.go
index ffcd3b0..51707f8 100644
--- a/openapi3/info.go
+++ b/openapi3/info.go
@@ -21,6 +21,15 @@ type Info struct {
 
 // MarshalJSON returns the JSON encoding of Info.
 func (info Info) MarshalJSON() ([]byte, error) {
+	x, err := info.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of Info.
+func (info Info) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 6+len(info.Extensions))
 	for k, v := range info.Extensions {
 		m[k] = v
@@ -39,7 +48,7 @@ func (info Info) MarshalJSON() ([]byte, error) {
 		m["license"] = x
 	}
 	m["version"] = info.Version
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets Info to a copy of data.
diff --git a/openapi3/license.go b/openapi3/license.go
index 3d2d2f0..e845ed8 100644
--- a/openapi3/license.go
+++ b/openapi3/license.go
@@ -17,6 +17,15 @@ type License struct {
 
 // MarshalJSON returns the JSON encoding of License.
 func (license License) MarshalJSON() ([]byte, error) {
+	x, err := license.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of License.
+func (license License) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 2+len(license.Extensions))
 	for k, v := range license.Extensions {
 		m[k] = v
@@ -25,7 +34,7 @@ func (license License) MarshalJSON() ([]byte, error) {
 	if x := license.URL; x != "" {
 		m["url"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets License to a copy of data.
diff --git a/openapi3/link.go b/openapi3/link.go
index 23a8df4..9615665 100644
--- a/openapi3/link.go
+++ b/openapi3/link.go
@@ -22,6 +22,15 @@ type Link struct {
 
 // MarshalJSON returns the JSON encoding of Link.
 func (link Link) MarshalJSON() ([]byte, error) {
+	x, err := link.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of Link.
+func (link Link) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 6+len(link.Extensions))
 	for k, v := range link.Extensions {
 		m[k] = v
@@ -46,7 +55,7 @@ func (link Link) MarshalJSON() ([]byte, error) {
 		m["requestBody"] = x
 	}
 
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets Link to a copy of data.
diff --git a/openapi3/maplike.go b/openapi3/maplike.go
index b27cbf6..8829b8d 100644
--- a/openapi3/maplike.go
+++ b/openapi3/maplike.go
@@ -76,8 +76,8 @@ func (responses Responses) JSONLookup(token string) (interface{}, error) {
 	}
 }
 
-// MarshalJSON returns the JSON encoding of Responses.
-func (responses *Responses) MarshalJSON() ([]byte, error) {
+// MarshalYAML returns the YAML encoding of Responses.
+func (responses *Responses) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, responses.Len()+len(responses.Extensions))
 	for k, v := range responses.Extensions {
 		m[k] = v
@@ -85,7 +85,16 @@ func (responses *Responses) MarshalJSON() ([]byte, error) {
 	for k, v := range responses.Map() {
 		m[k] = v
 	}
-	return json.Marshal(m)
+	return m, nil
+}
+
+// MarshalJSON returns the JSON encoding of Responses.
+func (responses *Responses) MarshalJSON() ([]byte, error) {
+	responsesYaml, err := responses.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(responsesYaml)
 }
 
 // UnmarshalJSON sets Responses to a copy of data.
@@ -195,8 +204,8 @@ func (callback Callback) JSONLookup(token string) (interface{}, error) {
 	}
 }
 
-// MarshalJSON returns the JSON encoding of Callback.
-func (callback *Callback) MarshalJSON() ([]byte, error) {
+// MarshalYAML returns the YAML encoding of Callback.
+func (callback *Callback) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, callback.Len()+len(callback.Extensions))
 	for k, v := range callback.Extensions {
 		m[k] = v
@@ -204,7 +213,16 @@ func (callback *Callback) MarshalJSON() ([]byte, error) {
 	for k, v := range callback.Map() {
 		m[k] = v
 	}
-	return json.Marshal(m)
+	return m, nil
+}
+
+// MarshalJSON returns the JSON encoding of Callback.
+func (callback *Callback) MarshalJSON() ([]byte, error) {
+	callbackYaml, err := callback.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(callbackYaml)
 }
 
 // UnmarshalJSON sets Callback to a copy of data.
@@ -314,8 +332,8 @@ func (paths Paths) JSONLookup(token string) (interface{}, error) {
 	}
 }
 
-// MarshalJSON returns the JSON encoding of Paths.
-func (paths *Paths) MarshalJSON() ([]byte, error) {
+// MarshalYAML returns the YAML encoding of Paths.
+func (paths *Paths) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, paths.Len()+len(paths.Extensions))
 	for k, v := range paths.Extensions {
 		m[k] = v
@@ -323,7 +341,16 @@ func (paths *Paths) MarshalJSON() ([]byte, error) {
 	for k, v := range paths.Map() {
 		m[k] = v
 	}
-	return json.Marshal(m)
+	return m, nil
+}
+
+// MarshalJSON returns the JSON encoding of Paths.
+func (paths *Paths) MarshalJSON() ([]byte, error) {
+	pathsYaml, err := paths.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(pathsYaml)
 }
 
 // UnmarshalJSON sets Paths to a copy of data.
diff --git a/openapi3/marsh.go b/openapi3/marsh.go
index 18036ae..9be7bb4 100644
--- a/openapi3/marsh.go
+++ b/openapi3/marsh.go
@@ -17,10 +17,18 @@ func unmarshalError(jsonUnmarshalErr error) error {
 }
 
 func unmarshal(data []byte, v interface{}) error {
+	var jsonErr, yamlErr error
+
 	// See https://github.com/getkin/kin-openapi/issues/680
-	if err := json.Unmarshal(data, v); err != nil {
-		// UnmarshalStrict(data, v) TODO: investigate how ymlv3 handles duplicate map keys
-		return yaml.Unmarshal(data, v)
+	if jsonErr = json.Unmarshal(data, v); jsonErr == nil {
+		return nil
+	}
+
+	// UnmarshalStrict(data, v) TODO: investigate how ymlv3 handles duplicate map keys
+	if yamlErr = yaml.Unmarshal(data, v); yamlErr == nil {
+		return nil
 	}
-	return nil
+
+	// If both unmarshaling attempts fail, return a new error that includes both errors
+	return fmt.Errorf("failed to unmarshal data: json error: %v, yaml error: %v", jsonErr, yamlErr)
 }
diff --git a/openapi3/media_type.go b/openapi3/media_type.go
index e043a7c..02de1db 100644
--- a/openapi3/media_type.go
+++ b/openapi3/media_type.go
@@ -65,6 +65,15 @@ func (mediaType *MediaType) WithEncoding(name string, enc *Encoding) *MediaType
 
 // MarshalJSON returns the JSON encoding of MediaType.
 func (mediaType MediaType) MarshalJSON() ([]byte, error) {
+	x, err := mediaType.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of MediaType.
+func (mediaType MediaType) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 4+len(mediaType.Extensions))
 	for k, v := range mediaType.Extensions {
 		m[k] = v
@@ -81,7 +90,7 @@ func (mediaType MediaType) MarshalJSON() ([]byte, error) {
 	if x := mediaType.Encoding; len(x) != 0 {
 		m["encoding"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets MediaType to a copy of data.
diff --git a/openapi3/openapi3.go b/openapi3/openapi3.go
index 04df350..04bac8f 100644
--- a/openapi3/openapi3.go
+++ b/openapi3/openapi3.go
@@ -55,6 +55,15 @@ func (doc *T) JSONLookup(token string) (interface{}, error) {
 
 // MarshalJSON returns the JSON encoding of T.
 func (doc *T) MarshalJSON() ([]byte, error) {
+	x, err := doc.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of T.
+func (doc *T) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 4+len(doc.Extensions))
 	for k, v := range doc.Extensions {
 		m[k] = v
@@ -77,7 +86,7 @@ func (doc *T) MarshalJSON() ([]byte, error) {
 	if x := doc.ExternalDocs; x != nil {
 		m["externalDocs"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets T to a copy of data.
diff --git a/openapi3/operation.go b/openapi3/operation.go
index d859a43..41e3c9b 100644
--- a/openapi3/operation.go
+++ b/openapi3/operation.go
@@ -58,6 +58,15 @@ func NewOperation() *Operation {
 
 // MarshalJSON returns the JSON encoding of Operation.
 func (operation Operation) MarshalJSON() ([]byte, error) {
+	x, err := operation.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of Operation.
+func (operation Operation) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 12+len(operation.Extensions))
 	for k, v := range operation.Extensions {
 		m[k] = v
@@ -96,7 +105,7 @@ func (operation Operation) MarshalJSON() ([]byte, error) {
 	if x := operation.ExternalDocs; x != nil {
 		m["externalDocs"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets Operation to a copy of data.
diff --git a/openapi3/parameter.go b/openapi3/parameter.go
index f5a157d..a13c112 100644
--- a/openapi3/parameter.go
+++ b/openapi3/parameter.go
@@ -150,6 +150,15 @@ func (parameter *Parameter) WithSchema(value *Schema) *Parameter {
 
 // MarshalJSON returns the JSON encoding of Parameter.
 func (parameter Parameter) MarshalJSON() ([]byte, error) {
+	x, err := parameter.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of Parameter.
+func (parameter Parameter) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 13+len(parameter.Extensions))
 	for k, v := range parameter.Extensions {
 		m[k] = v
@@ -195,7 +204,7 @@ func (parameter Parameter) MarshalJSON() ([]byte, error) {
 		m["content"] = x
 	}
 
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets Parameter to a copy of data.
diff --git a/openapi3/path_item.go b/openapi3/path_item.go
index e5dd0fb..0a6493a 100644
--- a/openapi3/path_item.go
+++ b/openapi3/path_item.go
@@ -31,8 +31,17 @@ type PathItem struct {
 
 // MarshalJSON returns the JSON encoding of PathItem.
 func (pathItem PathItem) MarshalJSON() ([]byte, error) {
+	x, err := pathItem.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of PathItem.
+func (pathItem PathItem) MarshalYAML() (interface{}, error) {
 	if ref := pathItem.Ref; ref != "" {
-		return json.Marshal(Ref{Ref: ref})
+		return Ref{Ref: ref}, nil
 	}
 
 	m := make(map[string]interface{}, 13+len(pathItem.Extensions))
@@ -78,7 +87,7 @@ func (pathItem PathItem) MarshalJSON() ([]byte, error) {
 	if x := pathItem.Parameters; len(x) != 0 {
 		m["parameters"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets PathItem to a copy of data.
diff --git a/openapi3/paths.go b/openapi3/paths.go
index daafe71..ac4f58b 100644
--- a/openapi3/paths.go
+++ b/openapi3/paths.go
@@ -218,21 +218,6 @@ func (paths *Paths) validateUniqueOperationIDs() error {
 	return nil
 }
 
-// Support YAML Marshaler interface for gopkg.in/yaml
-func (paths *Paths) MarshalYAML() (any, error) {
-	res := make(map[string]any, len(paths.Extensions)+len(paths.m))
-
-	for k, v := range paths.Extensions {
-		res[k] = v
-	}
-
-	for k, v := range paths.m {
-		res[k] = v
-	}
-
-	return res, nil
-}
-
 func normalizeTemplatedPath(path string) (string, uint, map[string]struct{}) {
 	if strings.IndexByte(path, '{') < 0 {
 		return path, 0, nil
diff --git a/openapi3/refs.go b/openapi3/refs.go
index a7e1e36..99237f1 100644
--- a/openapi3/refs.go
+++ b/openapi3/refs.go
@@ -27,15 +27,16 @@ func (x CallbackRef) MarshalYAML() (interface{}, error) {
 	if ref := x.Ref; ref != "" {
 		return &Ref{Ref: ref}, nil
 	}
-	return x.Value, nil
+	return x.Value.MarshalYAML()
 }
 
 // MarshalJSON returns the JSON encoding of CallbackRef.
 func (x CallbackRef) MarshalJSON() ([]byte, error) {
-	if ref := x.Ref; ref != "" {
-		return json.Marshal(Ref{Ref: ref})
+	y, err := x.MarshalYAML()
+	if err != nil {
+		return nil, err
 	}
-	return json.Marshal(x.Value)
+	return json.Marshal(y)
 }
 
 // UnmarshalJSON sets CallbackRef to a copy of data.
@@ -105,15 +106,16 @@ func (x ExampleRef) MarshalYAML() (interface{}, error) {
 	if ref := x.Ref; ref != "" {
 		return &Ref{Ref: ref}, nil
 	}
-	return x.Value, nil
+	return x.Value.MarshalYAML()
 }
 
 // MarshalJSON returns the JSON encoding of ExampleRef.
 func (x ExampleRef) MarshalJSON() ([]byte, error) {
-	if ref := x.Ref; ref != "" {
-		return json.Marshal(Ref{Ref: ref})
+	y, err := x.MarshalYAML()
+	if err != nil {
+		return nil, err
 	}
-	return x.Value.MarshalJSON()
+	return json.Marshal(y)
 }
 
 // UnmarshalJSON sets ExampleRef to a copy of data.
@@ -183,15 +185,16 @@ func (x HeaderRef) MarshalYAML() (interface{}, error) {
 	if ref := x.Ref; ref != "" {
 		return &Ref{Ref: ref}, nil
 	}
-	return x.Value, nil
+	return x.Value.MarshalYAML()
 }
 
 // MarshalJSON returns the JSON encoding of HeaderRef.
 func (x HeaderRef) MarshalJSON() ([]byte, error) {
-	if ref := x.Ref; ref != "" {
-		return json.Marshal(Ref{Ref: ref})
+	y, err := x.MarshalYAML()
+	if err != nil {
+		return nil, err
 	}
-	return x.Value.MarshalJSON()
+	return json.Marshal(y)
 }
 
 // UnmarshalJSON sets HeaderRef to a copy of data.
@@ -261,15 +264,16 @@ func (x LinkRef) MarshalYAML() (interface{}, error) {
 	if ref := x.Ref; ref != "" {
 		return &Ref{Ref: ref}, nil
 	}
-	return x.Value, nil
+	return x.Value.MarshalYAML()
 }
 
 // MarshalJSON returns the JSON encoding of LinkRef.
 func (x LinkRef) MarshalJSON() ([]byte, error) {
-	if ref := x.Ref; ref != "" {
-		return json.Marshal(Ref{Ref: ref})
+	y, err := x.MarshalYAML()
+	if err != nil {
+		return nil, err
 	}
-	return x.Value.MarshalJSON()
+	return json.Marshal(y)
 }
 
 // UnmarshalJSON sets LinkRef to a copy of data.
@@ -339,15 +343,16 @@ func (x ParameterRef) MarshalYAML() (interface{}, error) {
 	if ref := x.Ref; ref != "" {
 		return &Ref{Ref: ref}, nil
 	}
-	return x.Value, nil
+	return x.Value.MarshalYAML()
 }
 
 // MarshalJSON returns the JSON encoding of ParameterRef.
 func (x ParameterRef) MarshalJSON() ([]byte, error) {
-	if ref := x.Ref; ref != "" {
-		return json.Marshal(Ref{Ref: ref})
+	y, err := x.MarshalYAML()
+	if err != nil {
+		return nil, err
 	}
-	return x.Value.MarshalJSON()
+	return json.Marshal(y)
 }
 
 // UnmarshalJSON sets ParameterRef to a copy of data.
@@ -417,15 +422,16 @@ func (x RequestBodyRef) MarshalYAML() (interface{}, error) {
 	if ref := x.Ref; ref != "" {
 		return &Ref{Ref: ref}, nil
 	}
-	return x.Value, nil
+	return x.Value.MarshalYAML()
 }
 
 // MarshalJSON returns the JSON encoding of RequestBodyRef.
 func (x RequestBodyRef) MarshalJSON() ([]byte, error) {
-	if ref := x.Ref; ref != "" {
-		return json.Marshal(Ref{Ref: ref})
+	y, err := x.MarshalYAML()
+	if err != nil {
+		return nil, err
 	}
-	return x.Value.MarshalJSON()
+	return json.Marshal(y)
 }
 
 // UnmarshalJSON sets RequestBodyRef to a copy of data.
@@ -495,15 +501,16 @@ func (x ResponseRef) MarshalYAML() (interface{}, error) {
 	if ref := x.Ref; ref != "" {
 		return &Ref{Ref: ref}, nil
 	}
-	return x.Value, nil
+	return x.Value.MarshalYAML()
 }
 
 // MarshalJSON returns the JSON encoding of ResponseRef.
 func (x ResponseRef) MarshalJSON() ([]byte, error) {
-	if ref := x.Ref; ref != "" {
-		return json.Marshal(Ref{Ref: ref})
+	y, err := x.MarshalYAML()
+	if err != nil {
+		return nil, err
 	}
-	return x.Value.MarshalJSON()
+	return json.Marshal(y)
 }
 
 // UnmarshalJSON sets ResponseRef to a copy of data.
@@ -573,15 +580,16 @@ func (x SchemaRef) MarshalYAML() (interface{}, error) {
 	if ref := x.Ref; ref != "" {
 		return &Ref{Ref: ref}, nil
 	}
-	return x.Value, nil
+	return x.Value.MarshalYAML()
 }
 
 // MarshalJSON returns the JSON encoding of SchemaRef.
 func (x SchemaRef) MarshalJSON() ([]byte, error) {
-	if ref := x.Ref; ref != "" {
-		return json.Marshal(Ref{Ref: ref})
+	y, err := x.MarshalYAML()
+	if err != nil {
+		return nil, err
 	}
-	return x.Value.MarshalJSON()
+	return json.Marshal(y)
 }
 
 // UnmarshalJSON sets SchemaRef to a copy of data.
@@ -651,15 +659,16 @@ func (x SecuritySchemeRef) MarshalYAML() (interface{}, error) {
 	if ref := x.Ref; ref != "" {
 		return &Ref{Ref: ref}, nil
 	}
-	return x.Value, nil
+	return x.Value.MarshalYAML()
 }
 
 // MarshalJSON returns the JSON encoding of SecuritySchemeRef.
 func (x SecuritySchemeRef) MarshalJSON() ([]byte, error) {
-	if ref := x.Ref; ref != "" {
-		return json.Marshal(Ref{Ref: ref})
+	y, err := x.MarshalYAML()
+	if err != nil {
+		return nil, err
 	}
-	return x.Value.MarshalJSON()
+	return json.Marshal(y)
 }
 
 // UnmarshalJSON sets SecuritySchemeRef to a copy of data.
diff --git a/openapi3/request_body.go b/openapi3/request_body.go
index acd2d0e..7b8d353 100644
--- a/openapi3/request_body.go
+++ b/openapi3/request_body.go
@@ -75,6 +75,15 @@ func (requestBody *RequestBody) GetMediaType(mediaType string) *MediaType {
 
 // MarshalJSON returns the JSON encoding of RequestBody.
 func (requestBody RequestBody) MarshalJSON() ([]byte, error) {
+	x, err := requestBody.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of RequestBody.
+func (requestBody RequestBody) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 3+len(requestBody.Extensions))
 	for k, v := range requestBody.Extensions {
 		m[k] = v
@@ -88,7 +97,7 @@ func (requestBody RequestBody) MarshalJSON() ([]byte, error) {
 	if x := requestBody.Content; true {
 		m["content"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets RequestBody to a copy of data.
diff --git a/openapi3/response.go b/openapi3/response.go
index f69c237..6209b58 100644
--- a/openapi3/response.go
+++ b/openapi3/response.go
@@ -98,21 +98,6 @@ func (responses *Responses) Validate(ctx context.Context, opts ...ValidationOpti
 	return validateExtensions(ctx, responses.Extensions)
 }
 
-// Support YAML Marshaler interface for gopkg.in/yaml
-func (responses *Responses) MarshalYAML() (any, error) {
-	res := make(map[string]any, len(responses.Extensions)+len(responses.m))
-
-	for k, v := range responses.Extensions {
-		res[k] = v
-	}
-
-	for k, v := range responses.m {
-		res[k] = v
-	}
-
-	return res, nil
-}
-
 // Response is specified by OpenAPI/Swagger 3.0 standard.
 // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#response-object
 type Response struct {
@@ -150,6 +135,15 @@ func (response *Response) WithJSONSchemaRef(schema *SchemaRef) *Response {
 
 // MarshalJSON returns the JSON encoding of Response.
 func (response Response) MarshalJSON() ([]byte, error) {
+	x, err := response.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of Response.
+func (response Response) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 4+len(response.Extensions))
 	for k, v := range response.Extensions {
 		m[k] = v
@@ -166,7 +160,7 @@ func (response Response) MarshalJSON() ([]byte, error) {
 	if x := response.Links; len(x) != 0 {
 		m["links"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets Response to a copy of data.
diff --git a/openapi3/schema.go b/openapi3/schema.go
index beac7ba..8bacf72 100644
--- a/openapi3/schema.go
+++ b/openapi3/schema.go
@@ -222,23 +222,18 @@ func (addProps AdditionalProperties) MarshalYAML() (interface{}, error) {
 		return false, nil
 	}
 	if x := addProps.Schema; x != nil {
-		return x.Value, nil
+		return x.MarshalYAML()
 	}
 	return nil, nil
 }
 
 // MarshalJSON returns the JSON encoding of AdditionalProperties.
 func (addProps AdditionalProperties) MarshalJSON() ([]byte, error) {
-	if x := addProps.Has; x != nil {
-		if *x {
-			return []byte("true"), nil
-		}
-		return []byte("false"), nil
-	}
-	if x := addProps.Schema; x != nil {
-		return json.Marshal(x)
+	x, err := addProps.MarshalYAML()
+	if err != nil {
+		return nil, err
 	}
-	return nil, nil
+	return json.Marshal(x)
 }
 
 // UnmarshalJSON sets AdditionalProperties to a copy of data.
@@ -275,6 +270,16 @@ func NewSchema() *Schema {
 
 // MarshalJSON returns the JSON encoding of Schema.
 func (schema Schema) MarshalJSON() ([]byte, error) {
+	m, err := schema.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+
+	return json.Marshal(m)
+}
+
+// MarshalYAML returns the YAML encoding of Schema.
+func (schema Schema) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 36+len(schema.Extensions))
 	for k, v := range schema.Extensions {
 		m[k] = v
@@ -401,7 +406,7 @@ func (schema Schema) MarshalJSON() ([]byte, error) {
 		m["discriminator"] = x
 	}
 
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets Schema to a copy of data.
diff --git a/openapi3/security_scheme.go b/openapi3/security_scheme.go
index c07bfb6..7b6662c 100644
--- a/openapi3/security_scheme.go
+++ b/openapi3/security_scheme.go
@@ -52,6 +52,15 @@ func NewJWTSecurityScheme() *SecurityScheme {
 
 // MarshalJSON returns the JSON encoding of SecurityScheme.
 func (ss SecurityScheme) MarshalJSON() ([]byte, error) {
+	x, err := ss.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of SecurityScheme.
+func (ss SecurityScheme) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 8+len(ss.Extensions))
 	for k, v := range ss.Extensions {
 		m[k] = v
@@ -80,7 +89,7 @@ func (ss SecurityScheme) MarshalJSON() ([]byte, error) {
 	if x := ss.OpenIdConnectUrl; x != "" {
 		m["openIdConnectUrl"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets SecurityScheme to a copy of data.
@@ -225,6 +234,15 @@ const (
 
 // MarshalJSON returns the JSON encoding of OAuthFlows.
 func (flows OAuthFlows) MarshalJSON() ([]byte, error) {
+	x, err := flows.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of OAuthFlows.
+func (flows OAuthFlows) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 4+len(flows.Extensions))
 	for k, v := range flows.Extensions {
 		m[k] = v
@@ -241,7 +259,7 @@ func (flows OAuthFlows) MarshalJSON() ([]byte, error) {
 	if x := flows.AuthorizationCode; x != nil {
 		m["authorizationCode"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets OAuthFlows to a copy of data.
@@ -307,6 +325,15 @@ type OAuthFlow struct {
 
 // MarshalJSON returns the JSON encoding of OAuthFlow.
 func (flow OAuthFlow) MarshalJSON() ([]byte, error) {
+	x, err := flow.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of OAuthFlow.
+func (flow OAuthFlow) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 4+len(flow.Extensions))
 	for k, v := range flow.Extensions {
 		m[k] = v
@@ -321,7 +348,7 @@ func (flow OAuthFlow) MarshalJSON() ([]byte, error) {
 		m["refreshUrl"] = x
 	}
 	m["scopes"] = flow.Scopes
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets OAuthFlow to a copy of data.
diff --git a/openapi3/server.go b/openapi3/server.go
index 04e233d..85b716f 100644
--- a/openapi3/server.go
+++ b/openapi3/server.go
@@ -84,6 +84,15 @@ func (server *Server) BasePath() (string, error) {
 
 // MarshalJSON returns the JSON encoding of Server.
 func (server Server) MarshalJSON() ([]byte, error) {
+	x, err := server.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of Server.
+func (server Server) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 3+len(server.Extensions))
 	for k, v := range server.Extensions {
 		m[k] = v
@@ -95,7 +104,7 @@ func (server Server) MarshalJSON() ([]byte, error) {
 	if x := server.Variables; len(x) != 0 {
 		m["variables"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets Server to a copy of data.
@@ -234,6 +243,15 @@ type ServerVariable struct {
 
 // MarshalJSON returns the JSON encoding of ServerVariable.
 func (serverVariable ServerVariable) MarshalJSON() ([]byte, error) {
+	x, err := serverVariable.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of ServerVariable.
+func (serverVariable ServerVariable) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 4+len(serverVariable.Extensions))
 	for k, v := range serverVariable.Extensions {
 		m[k] = v
@@ -247,7 +265,7 @@ func (serverVariable ServerVariable) MarshalJSON() ([]byte, error) {
 	if x := serverVariable.Description; x != "" {
 		m["description"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets ServerVariable to a copy of data.
diff --git a/openapi3/tag.go b/openapi3/tag.go
index eea6462..8dc9967 100644
--- a/openapi3/tag.go
+++ b/openapi3/tag.go
@@ -42,6 +42,15 @@ type Tag struct {
 
 // MarshalJSON returns the JSON encoding of Tag.
 func (t Tag) MarshalJSON() ([]byte, error) {
+	x, err := t.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of Tag.
+func (t Tag) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 3+len(t.Extensions))
 	for k, v := range t.Extensions {
 		m[k] = v
@@ -55,7 +64,7 @@ func (t Tag) MarshalJSON() ([]byte, error) {
 	if x := t.ExternalDocs; x != nil {
 		m["externalDocs"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets Tag to a copy of data.
diff --git a/openapi3/xml.go b/openapi3/xml.go
index 604b607..e69c1fa 100644
--- a/openapi3/xml.go
+++ b/openapi3/xml.go
@@ -19,6 +19,15 @@ type XML struct {
 
 // MarshalJSON returns the JSON encoding of XML.
 func (xml XML) MarshalJSON() ([]byte, error) {
+	x, err := xml.MarshalYAML()
+	if err != nil {
+		return nil, err
+	}
+	return json.Marshal(x)
+}
+
+// MarshalYAML returns the YAML encoding of XML.
+func (xml XML) MarshalYAML() (interface{}, error) {
 	m := make(map[string]interface{}, 5+len(xml.Extensions))
 	for k, v := range xml.Extensions {
 		m[k] = v
@@ -38,7 +47,7 @@ func (xml XML) MarshalJSON() ([]byte, error) {
 	if x := xml.Wrapped; x {
 		m["wrapped"] = x
 	}
-	return json.Marshal(m)
+	return m, nil
 }
 
 // UnmarshalJSON sets XML to a copy of data.
diff --git a/refs.sh b/refs.sh
index 9ece26e..a28ef3f 100755
--- a/refs.sh
+++ b/refs.sh
@@ -46,29 +46,17 @@ func (x ${type}Ref) MarshalYAML() (interface{}, error) {
 	if ref := x.Ref; ref != "" {
 		return &Ref{Ref: ref}, nil
 	}
-	return x.Value, nil
+	return x.Value.MarshalYAML()
 }
 
 // MarshalJSON returns the JSON encoding of ${type}Ref.
 func (x ${type}Ref) MarshalJSON() ([]byte, error) {
-	if ref := x.Ref; ref != "" {
-		return json.Marshal(Ref{Ref: ref})
+	y, err := x.MarshalYAML()
+	if err != nil {
+		return nil, err
 	}
+	return json.Marshal(y)
 EOF
-
-	case $type in
-		# Callback) echo '	return x.Value.MarshalJSON()' ;; TODO: when https://github.com/getkin/kin-openapi/issues/687
-		Example) echo '	return x.Value.MarshalJSON()' ;;
-		Header) echo '	return x.Value.MarshalJSON()' ;;
-		Link) echo '	return x.Value.MarshalJSON()' ;;
-		Parameter) echo '	return x.Value.MarshalJSON()' ;;
-		RequestBody) echo '	return x.Value.MarshalJSON()' ;;
-		Response) echo '	return x.Value.MarshalJSON()' ;;
-		Schema) echo '	return x.Value.MarshalJSON()' ;;
-		SecurityScheme) echo '	return x.Value.MarshalJSON()' ;;
-		*) echo '	return json.Marshal(x.Value)'
-	esac
-
 	cat <<EOF
 }
 


Test patch:
diff --git a/openapi3/testdata/issue241.yml b/openapi3/testdata/issue241.yml
index 07609c1..1bcd73d 100644
--- a/openapi3/testdata/issue241.yml
+++ b/openapi3/testdata/issue241.yml
@@ -1,15 +1,15 @@
-openapi: 3.0.3
 components:
   schemas:
     FooBar:
-      type: object
       properties:
         type_url:
           type: string
         value:
-          type: string
           format: byte
+          type: string
+      type: object
 info:
   title: sample
   version: version not set
+openapi: 3.0.3
 paths: {}