Repository: ogen-go/ogen
Pull request #483 (2022-07-14 10:49:52)

feat(ogen): parse `XML`, `Link`, `Callback`


Patch:
diff --git a/dsl.go b/dsl.go
index 964b88a3..df2dbd11 100644
--- a/dsl.go
+++ b/dsl.go
@@ -733,7 +733,7 @@ func (r *Response) initContent() {
 }
 
 // SetLinks sets the Links of the Response.
-func (r *Response) SetLinks(l map[string]interface{}) *Response {
+func (r *Response) SetLinks(l map[string]*Link) *Response {
 	r.Links = l
 	return r
 }
diff --git a/gen/gen_security.go b/gen/gen_security.go
index 8021753b..57e5d48f 100644
--- a/gen/gen_security.go
+++ b/gen/gen_security.go
@@ -9,7 +9,7 @@ import (
 	"github.com/ogen-go/ogen/openapi"
 )
 
-func (g *Generator) generateSecurityAPIKey(s *ir.Security, spec openapi.SecurityRequirements) (*ir.Security, error) {
+func (g *Generator) generateSecurityAPIKey(s *ir.Security, spec openapi.SecurityRequirement) (*ir.Security, error) {
 	security := spec.Security
 	if name := security.Name; name == "" {
 		return nil, errors.Errorf(`invalid "apiKey" name %q`, name)
@@ -34,7 +34,7 @@ func (g *Generator) generateSecurityAPIKey(s *ir.Security, spec openapi.Security
 	return s, nil
 }
 
-func (g *Generator) generateSecurityHTTP(s *ir.Security, spec openapi.SecurityRequirements) (*ir.Security, error) {
+func (g *Generator) generateSecurityHTTP(s *ir.Security, spec openapi.SecurityRequirement) (*ir.Security, error) {
 	security := spec.Security
 	s.Kind = ir.HeaderSecurity
 	switch scheme := security.Scheme; scheme {
@@ -64,7 +64,7 @@ func (g *Generator) generateSecurityHTTP(s *ir.Security, spec openapi.SecurityRe
 	return s, nil
 }
 
-func (g *Generator) generateSecurity(ctx *genctx, spec openapi.SecurityRequirements) (r *ir.Security, rErr error) {
+func (g *Generator) generateSecurity(ctx *genctx, spec openapi.SecurityRequirement) (r *ir.Security, rErr error) {
 	if sec, ok := g.securities[spec.Name]; ok {
 		return sec, nil
 	}
@@ -110,7 +110,7 @@ func (g *Generator) generateSecurity(ctx *genctx, spec openapi.SecurityRequireme
 	}
 }
 
-func (g *Generator) generateSecurities(ctx *genctx, spec []openapi.SecurityRequirements) (r []*ir.SecurityRequirement, _ error) {
+func (g *Generator) generateSecurities(ctx *genctx, spec []openapi.SecurityRequirement) (r []*ir.SecurityRequirement, _ error) {
 	for idx, sr := range spec {
 		s, err := g.generateSecurity(ctx, sr)
 		if err != nil {
diff --git a/gen/ir/security.go b/gen/ir/security.go
index e41fd0b4..b8007072 100644
--- a/gen/ir/security.go
+++ b/gen/ir/security.go
@@ -81,5 +81,5 @@ func (s *Security) GoDoc() []string {
 
 type SecurityRequirement struct {
 	Security *Security
-	Spec     openapi.SecurityRequirements
+	Spec     openapi.SecurityRequirement
 }
diff --git a/jsonschema/errors.go b/jsonschema/errors.go
index 5644c925..a35481af 100644
--- a/jsonschema/errors.go
+++ b/jsonschema/errors.go
@@ -1,14 +1,17 @@
 package jsonschema
 
 import (
+	"github.com/go-faster/errors"
+
 	"github.com/ogen-go/ogen/internal/location"
 )
 
 // LocationError is a wrapper for an error that has a location.
 type LocationError = location.Error
 
-func (p *Parser) wrapLocation(filename string, l location.Locatable, err error) error {
-	if err == nil || l == nil || p == nil {
+func (p *Parser) wrapLocation(filename string, l location.Locator, err error) error {
+	var locErr *LocationError
+	if err == nil || p == nil || errors.As(err, &locErr) {
 		return err
 	}
 	loc, ok := l.Location()
diff --git a/jsonschema/parser.go b/jsonschema/parser.go
index a1fd145e..ac54589f 100644
--- a/jsonschema/parser.go
+++ b/jsonschema/parser.go
@@ -49,7 +49,7 @@ func (p *Parser) Resolve(ref string) (*Schema, error) {
 func (p *Parser) parse(schema *RawSchema, ctx *resolveCtx) (_ *Schema, rerr error) {
 	if schema != nil {
 		defer func() {
-			rerr = p.wrapLocation(ctx.lastLoc(), &schema.Locator, rerr)
+			rerr = p.wrapLocation(ctx.lastLoc(), schema.Locator, rerr)
 		}()
 	}
 	return p.parse1(schema, ctx, func(s *Schema) *Schema {
@@ -73,15 +73,24 @@ func (p *Parser) parse1(schema *RawSchema, ctx *resolveCtx, hook func(*Schema) *
 			handleNullableEnum(s)
 		}
 		if d := schema.Default; len(d) > 0 {
-			v, err := parseJSONValue(s, json.RawMessage(d))
-			if err != nil {
-				return nil, errors.Wrap(err, "parse default")
-			}
-			if v == nil && !s.Nullable {
-				return nil, errors.New("unexpected default \"null\" value")
+			if err := func() error {
+				v, err := parseJSONValue(s, json.RawMessage(d))
+				if err != nil {
+					return err
+				}
+
+				if v == nil && !s.Nullable {
+					return errors.New("unexpected default \"null\" value")
+				}
+
+				s.Default = v
+				s.DefaultSet = true
+				return nil
+			}(); err != nil {
+				loc := schema.Locator.Field("default")
+				err := errors.Wrap(err, "parse default")
+				return nil, p.wrapLocation(ctx.lastLoc(), loc, err)
 			}
-			s.Default = v
-			s.DefaultSet = true
 		}
 		if a, ok := schema.XAnnotations["x-ogen-name"]; ok {
 			name, err := jx.DecodeBytes(a).Str()
@@ -401,6 +410,15 @@ func (p *Parser) extendInfo(schema *RawSchema, s *Schema) *Schema {
 			Mapping:      d.Mapping,
 		}
 	}
+	if x := schema.XML; x != nil {
+		s.XML = &XML{
+			Name:      x.Name,
+			Namespace: x.Namespace,
+			Prefix:    x.Prefix,
+			Attribute: x.Attribute,
+			Wrapped:   x.Wrapped,
+		}
+	}
 
 	s.Locator = schema.Locator
 	return s
@@ -418,7 +436,7 @@ func parseType(typ string) (SchemaType, error) {
 
 	t, ok := mapping[typ]
 	if !ok {
-		return SchemaType(""), errors.Errorf("unexpected type: %q", typ)
+		return "", errors.Errorf("unexpected type: %q", typ)
 	}
 
 	return t, nil
diff --git a/jsonschema/raw_schema.go b/jsonschema/raw_schema.go
index 447b0198..17bbda0d 100644
--- a/jsonschema/raw_schema.go
+++ b/jsonschema/raw_schema.go
@@ -26,7 +26,6 @@ type RawSchema struct {
 	AllOf                []*RawSchema          `json:"allOf,omitempty" yaml:"allOf,omitempty"`
 	OneOf                []*RawSchema          `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
 	AnyOf                []*RawSchema          `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
-	Discriminator        *Discriminator        `json:"discriminator,omitempty" yaml:"discriminator,omitempty"`
 	Enum                 Enum                  `json:"enum,omitempty" yaml:"enum,omitempty"`
 	MultipleOf           Num                   `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"`
 	Maximum              Num                   `json:"maximum,omitempty" yaml:"maximum,omitempty"`
@@ -42,11 +41,14 @@ type RawSchema struct {
 	MaxProperties        *uint64               `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"`
 	MinProperties        *uint64               `json:"minProperties,omitempty" yaml:"minProperties,omitempty"`
 	Default              Default               `json:"default,omitempty" yaml:"default,omitempty"`
-	Example              Example               `json:"example,omitempty" yaml:"example,omitempty"`
 	Deprecated           bool                  `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
 	ContentEncoding      string                `json:"contentEncoding,omitempty" yaml:"contentEncoding,omitempty"`
 	ContentMediaType     string                `json:"contentMediaType,omitempty" yaml:"contentMediaType,omitempty"`
 
+	Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"`
+	XML           *XML           `json:"xml,omitempty" yaml:"xml,omitempty"`
+	Example       Example        `json:"example,omitempty" yaml:"example,omitempty"`
+
 	XAnnotations map[string]json.RawMessage `json:"-" yaml:"-"`
 	Locator      location.Locator           `json:"-" yaml:",inline"`
 }
@@ -245,8 +247,44 @@ func (p *RawPatternProperties) UnmarshalJSON(data []byte) error {
 	})
 }
 
-// Discriminator is JSON Schema discriminator description.
+// Discriminator discriminates types for OneOf, AllOf, AnyOf.
+//
+// See https://spec.openapis.org/oas/v3.1.0#discriminator-object.
 type Discriminator struct {
-	PropertyName string            `json:"propertyName" yaml:"propertyName"`
-	Mapping      map[string]string `json:"mapping,omitempty" yaml:"mapping,omitempty"`
+	// REQUIRED. The name of the property in the payload that will hold the discriminator value.
+	PropertyName string `json:"propertyName" yaml:"propertyName"`
+	// An object to hold mappings between payload values and schema names or references.
+	Mapping map[string]string `json:"mapping,omitempty" yaml:"mapping,omitempty"`
+}
+
+// XML is a metadata object that allows for more fine-tuned XML model definitions.
+//
+// See https://spec.openapis.org/oas/v3.1.0#xml-object.
+type XML struct {
+	// Replaces the name of the element/attribute used for the described schema property.
+	//
+	// When defined within items, it will affect the name of the individual XML elements within the list.
+	//
+	// When defined alongside type being array (outside the items), it will affect the wrapping element
+	// and only if wrapped is true.
+	//
+	// If wrapped is false, it will be ignored.
+	Name string `json:"name,omitempty" yaml:"name,omitempty"`
+	// The URI of the namespace definition.
+	//
+	// This MUST be in the form of an absolute URI.
+	Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
+	// The prefix to be used for the name.
+	Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
+	// Declares whether the property definition translates to an attribute instead of an element.
+	//
+	// Default value is false.
+	Attribute bool `json:"attribute,omitempty" yaml:"attribute,omitempty"`
+	// MAY be used only for an array definition. Signifies whether the array is wrapped
+	// (for example, `<books><book/><book/></books>`) or unwrapped (`<book/><book/>`).
+	//
+	// The definition takes effect only when defined alongside type being array (outside the items).
+	//
+	// Default value is false.
+	Wrapped bool `json:"wrapped,omitempty" yaml:"wrapped,omitempty"`
 }
diff --git a/jsonschema/schema.go b/jsonschema/schema.go
index 7abd7f34..ba6d98c0 100644
--- a/jsonschema/schema.go
+++ b/jsonschema/schema.go
@@ -51,10 +51,12 @@ type Schema struct {
 
 	Nullable bool // Whether schema is nullable or not. Any types.
 
-	OneOf         []*Schema
-	AnyOf         []*Schema
-	AllOf         []*Schema
+	OneOf []*Schema
+	AnyOf []*Schema
+	AllOf []*Schema
+
 	Discriminator *Discriminator
+	XML           *XML
 
 	// Numeric validation (Integer, Number).
 	Maximum          Num
diff --git a/openapi/operation.go b/openapi/operation.go
index abafb3b3..e12a562e 100644
--- a/openapi/operation.go
+++ b/openapi/operation.go
@@ -15,7 +15,7 @@ type Operation struct {
 	RequestBody *RequestBody // optional
 
 	// Security requirements.
-	Security []SecurityRequirements
+	Security []SecurityRequirement
 
 	// Operation responses.
 	// Map is always non-nil.
diff --git a/openapi/parameter.go b/openapi/parameter.go
index a476bd63..e9a3edf8 100644
--- a/openapi/parameter.go
+++ b/openapi/parameter.go
@@ -35,15 +35,16 @@ func (l ParameterLocation) Cookie() bool { return l == LocationCookie }
 type Parameter struct {
 	Ref string
 
-	Name        string
-	Description string
-	Deprecated  bool
-	Schema      *jsonschema.Schema
-	Content     *ParameterContent
-	In          ParameterLocation
-	Style       ParameterStyle
-	Explode     bool
-	Required    bool
+	Name          string
+	Description   string
+	Deprecated    bool
+	Schema        *jsonschema.Schema
+	Content       *ParameterContent
+	In            ParameterLocation
+	Style         ParameterStyle
+	Explode       bool
+	Required      bool
+	AllowReserved bool
 
 	location.Locator `json:"-" yaml:"-"`
 }
diff --git a/openapi/parser/errors.go b/openapi/parser/errors.go
index 8cfb27a6..6e15653d 100644
--- a/openapi/parser/errors.go
+++ b/openapi/parser/errors.go
@@ -1,6 +1,8 @@
 package parser
 
 import (
+	"github.com/go-faster/errors"
+
 	"github.com/ogen-go/ogen/internal/location"
 )
 
@@ -8,9 +10,12 @@ import (
 type LocationError = location.Error
 
 func (p *parser) wrapLocation(filename string, l location.Locator, err error) error {
-	if err == nil || p == nil {
+	var locErr *LocationError
+	// Do not wrap error if it is nil or is already a LocationError.
+	if err == nil || p == nil || errors.As(err, &locErr) {
 		return err
 	}
+
 	loc, ok := l.Location()
 	if !ok {
 		return err
diff --git a/openapi/parser/parse_components.go b/openapi/parser/parse_components.go
index 95ef0fdc..249cdea9 100644
--- a/openapi/parser/parse_components.go
+++ b/openapi/parser/parse_components.go
@@ -9,6 +9,19 @@ import (
 )
 
 func (p *parser) parseComponents(c *ogen.Components) (_ *openapi.Components, rerr error) {
+	if c == nil {
+		return &openapi.Components{
+			Schemas:       map[string]*jsonschema.Schema{},
+			Responses:     map[string]*openapi.Response{},
+			Parameters:    map[string]*openapi.Parameter{},
+			Examples:      map[string]*openapi.Example{},
+			RequestBodies: map[string]*openapi.RequestBody{},
+		}, nil
+	}
+	defer func() {
+		rerr = p.wrapLocation("", c.Locator, rerr)
+	}()
+
 	result := &openapi.Components{
 		Schemas:       make(map[string]*jsonschema.Schema, len(c.Schemas)),
 		Responses:     make(map[string]*openapi.Response, len(c.Responses)),
@@ -16,17 +29,17 @@ func (p *parser) parseComponents(c *ogen.Components) (_ *openapi.Components, rer
 		Examples:      make(map[string]*openapi.Example, len(c.Examples)),
 		RequestBodies: make(map[string]*openapi.RequestBody, len(c.RequestBodies)),
 	}
-	if c != nil {
-		defer func() {
-			rerr = p.wrapLocation("", c.Locator, rerr)
-		}()
+	wrapErr := func(component, name string, err error) error {
+		loc := c.Locator.Field(component).Field(name)
+		err = errors.Wrapf(err, "schemas: %q", name)
+		return p.wrapLocation("", loc, err)
 	}
 
 	for name := range c.Schemas {
 		ref := "#/components/schemas/" + name
 		s, err := p.schemaParser.Resolve(ref)
 		if err != nil {
-			return nil, errors.Wrapf(err, "schemas: %q", name)
+			return nil, wrapErr("schemas", name, err)
 		}
 
 		result.Schemas[name] = s
@@ -36,7 +49,7 @@ func (p *parser) parseComponents(c *ogen.Components) (_ *openapi.Components, rer
 		ref := "#/components/responses/" + name
 		r, err := p.resolveResponse(ref, newResolveCtx(p.depthLimit))
 		if err != nil {
-			return nil, errors.Wrapf(err, "responses: %q", name)
+			return nil, wrapErr("responses", name, err)
 		}
 
 		result.Responses[name] = r
@@ -46,7 +59,7 @@ func (p *parser) parseComponents(c *ogen.Components) (_ *openapi.Components, rer
 		ref := "#/components/parameters/" + name
 		pp, err := p.resolveParameter(ref, newResolveCtx(p.depthLimit))
 		if err != nil {
-			return nil, errors.Wrapf(err, "parameters: %q", name)
+			return nil, wrapErr("parameters", name, err)
 		}
 
 		result.Parameters[name] = pp
@@ -56,7 +69,7 @@ func (p *parser) parseComponents(c *ogen.Components) (_ *openapi.Components, rer
 		ref := "#/components/examples/" + name
 		ex, err := p.resolveExample(ref, newResolveCtx(p.depthLimit))
 		if err != nil {
-			return nil, errors.Wrapf(err, "examples: %q", name)
+			return nil, wrapErr("examples", name, err)
 		}
 
 		result.Examples[name] = ex
@@ -66,7 +79,7 @@ func (p *parser) parseComponents(c *ogen.Components) (_ *openapi.Components, rer
 		ref := "#/components/requestBodies/" + name
 		b, err := p.resolveRequestBody(ref, newResolveCtx(p.depthLimit))
 		if err != nil {
-			return nil, errors.Wrapf(err, "requestBodies: %q", name)
+			return nil, wrapErr("requestBodies", name, err)
 		}
 
 		result.RequestBodies[name] = b
diff --git a/openapi/parser/parse_header.go b/openapi/parser/parse_header.go
index a536f88f..6e8d2945 100644
--- a/openapi/parser/parse_header.go
+++ b/openapi/parser/parse_header.go
@@ -13,8 +13,8 @@ func (p *parser) parseHeaders(headers map[string]*ogen.Header, ctx *resolveCtx)
 	}
 
 	result := make(map[string]*openapi.Header, len(headers))
-	for name, m := range headers {
-		result[name], err = p.parseHeader(name, m, ctx)
+	for name, h := range headers {
+		result[name], err = p.parseHeader(name, h, ctx)
 		if err != nil {
 			return nil, errors.Wrapf(err, "header %q", name)
 		}
@@ -59,12 +59,14 @@ func (p *parser) parseHeader(name string, header *ogen.Header, ctx *resolveCtx)
 
 	schema, err := p.parseSchema(header.Schema, ctx)
 	if err != nil {
-		return nil, errors.Wrap(err, "schema")
+		err := errors.Wrap(err, "schema")
+		return nil, p.wrapField("schema", ctx.lastLoc(), header.Locator, err)
 	}
 
-	content, err := p.parseParameterContent(header.Content, ctx)
+	content, err := p.parseParameterContent(header.Content, header.Locator.Field("content"), ctx)
 	if err != nil {
-		return nil, errors.Wrap(err, "content")
+		err := errors.Wrap(err, "content")
+		return nil, p.wrapField("content", ctx.lastLoc(), header.Locator, err)
 	}
 
 	op := &openapi.Header{
diff --git a/openapi/parser/parse_mediatype.go b/openapi/parser/parse_mediatype.go
index 829366c3..24e68513 100644
--- a/openapi/parser/parse_mediatype.go
+++ b/openapi/parser/parse_mediatype.go
@@ -7,11 +7,16 @@ import (
 	"github.com/go-faster/errors"
 
 	"github.com/ogen-go/ogen"
+	"github.com/ogen-go/ogen/internal/location"
 	"github.com/ogen-go/ogen/jsonschema"
 	"github.com/ogen-go/ogen/openapi"
 )
 
-func (p *parser) parseContent(content map[string]ogen.Media, ctx *resolveCtx) (_ map[string]*openapi.MediaType, err error) {
+func (p *parser) parseContent(
+	content map[string]ogen.Media,
+	locator location.Locator,
+	ctx *resolveCtx,
+) (_ map[string]*openapi.MediaType, err error) {
 	if len(content) == 0 {
 		return nil, nil
 	}
@@ -19,7 +24,8 @@ func (p *parser) parseContent(content map[string]ogen.Media, ctx *resolveCtx) (_
 	result := make(map[string]*openapi.MediaType, len(content))
 	for name, m := range content {
 		if _, _, err := mime.ParseMediaType(name); err != nil {
-			return nil, errors.Wrapf(err, "content type %q", name)
+			err := errors.Wrapf(err, "content type %q", name)
+			return nil, p.wrapLocation(ctx.lastLoc(), locator.Key(name), err)
 		}
 
 		result[name], err = p.parseMediaType(m, ctx)
@@ -31,17 +37,28 @@ func (p *parser) parseContent(content map[string]ogen.Media, ctx *resolveCtx) (_
 	return result, nil
 }
 
-func (p *parser) parseParameterContent(content map[string]ogen.Media, ctx *resolveCtx) (*openapi.ParameterContent, error) {
+func (p *parser) parseParameterContent(
+	content map[string]ogen.Media,
+	locator location.Locator,
+	ctx *resolveCtx,
+) (*openapi.ParameterContent, error) {
 	if content == nil {
 		return nil, nil
 	}
 	if len(content) != 1 {
-		return nil, errors.New(`"content" map MUST only contain one entry`)
+		for key := range content {
+			locator = locator.Field(key)
+			break
+		}
+
+		err := errors.New(`"content" map MUST only contain one entry`)
+		return nil, p.wrapLocation(ctx.lastLoc(), locator, err)
 	}
 
 	for name, m := range content {
 		if _, _, err := mime.ParseMediaType(name); err != nil {
-			return nil, errors.Wrapf(err, "content type %q", name)
+			err := errors.Wrapf(err, "content type %q", name)
+			return nil, p.wrapLocation(ctx.lastLoc(), locator.Key(name), err)
 		}
 
 		media, err := p.parseMediaType(m, ctx)
@@ -74,16 +91,11 @@ func (p *parser) parseMediaType(m ogen.Media, ctx *resolveCtx) (_ *openapi.Media
 			names[prop.Name] = prop
 		}
 
-		parseEncoding := func(name string, e ogen.Encoding) (rerr error) {
+		parseEncoding := func(name string, prop jsonschema.Property, e ogen.Encoding) (rerr error) {
 			defer func() {
 				rerr = p.wrapLocation(ctx.lastLoc(), e.Locator, rerr)
 			}()
 
-			prop, ok := names[name]
-			if !ok {
-				return errors.Errorf("unknown property %q", name)
-			}
-
 			encoding := &openapi.Encoding{
 				ContentType:   e.ContentType,
 				Headers:       map[string]*openapi.Header{},
@@ -99,13 +111,14 @@ func (p *parser) parseMediaType(m ogen.Media, ctx *resolveCtx) (_ *openapi.Media
 			encodings[name] = encoding
 
 			if err := p.validateParamStyle(&openapi.Parameter{
-				Name:     name,
-				Schema:   prop.Schema,
-				In:       openapi.LocationQuery,
-				Style:    encoding.Style,
-				Explode:  encoding.Explode,
-				Required: prop.Required,
-				Locator:  encoding.Locator,
+				Name:          name,
+				Schema:        prop.Schema,
+				In:            openapi.LocationQuery,
+				Style:         encoding.Style,
+				Explode:       encoding.Explode,
+				Required:      prop.Required,
+				AllowReserved: encoding.AllowReserved,
+				Locator:       encoding.Locator,
 			}, ctx.lastLoc()); err != nil {
 				return errors.Wrap(err, "param style")
 			}
@@ -113,8 +126,16 @@ func (p *parser) parseMediaType(m ogen.Media, ctx *resolveCtx) (_ *openapi.Media
 			return nil
 		}
 
+		encodingLoc := m.Locator.Field("encoding")
 		for name, e := range m.Encoding {
-			if err := parseEncoding(name, e); err != nil {
+			prop, ok := names[name]
+			if !ok {
+				loc := encodingLoc.Key(name)
+				err := errors.Errorf("unknown property %q", name)
+				return nil, p.wrapLocation(ctx.lastLoc(), loc, err)
+			}
+
+			if err := parseEncoding(name, prop, e); err != nil {
 				return nil, errors.Wrapf(err, "encoding property %q", name)
 			}
 		}
diff --git a/openapi/parser/parse_parameter.go b/openapi/parser/parse_parameter.go
index efc733a8..c0a25739 100644
--- a/openapi/parser/parse_parameter.go
+++ b/openapi/parser/parse_parameter.go
@@ -7,6 +7,7 @@ import (
 	"golang.org/x/net/http/httpguts"
 
 	"github.com/ogen-go/ogen"
+	"github.com/ogen-go/ogen/internal/location"
 	"github.com/ogen-go/ogen/jsonschema"
 	"github.com/ogen-go/ogen/openapi"
 )
@@ -33,7 +34,11 @@ func mergeParams(opParams, itemParams []*openapi.Parameter) []*openapi.Parameter
 	return opParams
 }
 
-func (p *parser) parseParams(params []*ogen.Parameter, ctx *resolveCtx) ([]*openapi.Parameter, error) {
+func (p *parser) parseParams(
+	params []*ogen.Parameter,
+	locator location.Locator,
+	ctx *resolveCtx,
+) ([]*openapi.Parameter, error) {
 	// Unique parameter is defined by a combination of a name and location.
 	type pnameLoc struct {
 		name     string
@@ -47,7 +52,9 @@ func (p *parser) parseParams(params []*ogen.Parameter, ctx *resolveCtx) ([]*open
 
 	for idx, spec := range params {
 		if spec == nil {
-			return nil, errors.Errorf("parameter %d is empty or null", idx)
+			loc := locator.Index(idx)
+			err := errors.Errorf("parameter %d is empty or null", idx)
+			return nil, p.wrapLocation(ctx.lastLoc(), loc, err)
 		}
 
 		param, err := p.parseParameter(spec, ctx)
@@ -60,7 +67,7 @@ func (p *parser) parseParams(params []*ogen.Parameter, ctx *resolveCtx) ([]*open
 			location: param.In,
 		}
 		if _, ok := unique[ploc]; ok {
-			err = errors.Errorf("duplicate parameter: %q in %q", param.Name, param.In)
+			err := errors.Errorf("duplicate parameter: %q in %q", param.Name, param.In)
 			return nil, p.wrapLocation(ctx.lastLoc(), spec.Locator, err)
 		}
 
@@ -75,18 +82,18 @@ func (p *parser) validateParameter(
 	name string,
 	locatedIn openapi.ParameterLocation,
 	param *ogen.Parameter,
-	loc string,
+	file string,
 ) error {
 	switch {
 	case param.Schema != nil && param.Content != nil:
 		err := errors.New("parameter MUST contain either a schema property, or a content property, but not both")
-		return p.wrapField("schema", loc, param.Locator, err)
+		return p.wrapField("schema", file, param.Locator, err)
 	case param.Schema == nil && param.Content == nil:
 		return errors.New("parameter MUST contain either a schema property, or a content property")
 	case param.Content != nil && len(param.Content) < 1:
 		// https://github.com/OAI/OpenAPI-Specification/discussions/2875
 		err := errors.New("content must have at least one entry")
-		return p.wrapField("content", loc, param.Locator, err)
+		return p.wrapField("content", file, param.Locator, err)
 	}
 
 	// Path parameters are always required.
@@ -94,11 +101,12 @@ func (p *parser) validateParameter(
 	case openapi.LocationPath:
 		if !param.Required {
 			err := errors.New("path parameters must be required")
-			return p.wrapField("required", loc, param.Locator, err)
+			return p.wrapField("required", file, param.Locator, err)
 		}
 	case openapi.LocationHeader:
 		if !httpguts.ValidHeaderFieldName(name) {
-			return errors.Errorf("invalid header name %q", name)
+			err := errors.Errorf("invalid header name %q", name)
+			return p.wrapField("name", file, param.Locator, err)
 		}
 	}
 	return nil
@@ -138,26 +146,28 @@ func (p *parser) parseParameter(param *ogen.Parameter, ctx *resolveCtx) (_ *open
 
 	schema, err := p.parseSchema(param.Schema, ctx)
 	if err != nil {
-		err = errors.Wrap(err, "schema")
+		err := errors.Wrap(err, "schema")
 		return nil, p.wrapField("schema", ctx.lastLoc(), param.Locator, err)
 	}
 
-	content, err := p.parseParameterContent(param.Content, ctx)
+	content, err := p.parseParameterContent(param.Content, param.Locator.Field("content"), ctx)
 	if err != nil {
-		err = errors.Wrap(err, "content")
+		err := errors.Wrap(err, "content")
 		return nil, p.wrapField("content", ctx.lastLoc(), param.Locator, err)
 	}
 
 	op := &openapi.Parameter{
-		Name:        param.Name,
-		Description: param.Description,
-		Schema:      schema,
-		Content:     content,
-		In:          locatedIn,
-		Style:       inferParamStyle(locatedIn, param.Style),
-		Explode:     inferParamExplode(locatedIn, param.Explode),
-		Required:    param.Required,
-		Deprecated:  param.Deprecated,
+		Name:          param.Name,
+		Description:   param.Description,
+		Deprecated:    param.Deprecated,
+		Schema:        schema,
+		Content:       content,
+		In:            locatedIn,
+		Style:         inferParamStyle(locatedIn, param.Style),
+		Explode:       inferParamExplode(locatedIn, param.Explode),
+		Required:      param.Required,
+		AllowReserved: param.AllowReserved,
+		Locator:       param.Locator,
 	}
 
 	if param.Content != nil {
@@ -252,7 +262,8 @@ func (p *parser) validateParamStyle(param *openapi.Parameter, loc string) error
 
 	types, ok := styles[stexp{param.Style, param.Explode}]
 	if !ok {
-		return errors.Errorf("invalid style explode combination %q, explode:%v", param.Style, param.Explode)
+		err := errors.Errorf("invalid style explode combination %q, explode:%v", param.Style, param.Explode)
+		return wrap("style", err)
 	}
 
 	allowed := func(t byte) bool { return types&t != 0 }
diff --git a/openapi/parser/parse_request_body.go b/openapi/parser/parse_request_body.go
index 221f89e1..19be0762 100644
--- a/openapi/parser/parse_request_body.go
+++ b/openapi/parser/parse_request_body.go
@@ -29,7 +29,7 @@ func (p *parser) parseRequestBody(body *ogen.RequestBody, ctx *resolveCtx) (_ *o
 			return nil, errors.New("content must have at least one entry")
 		}
 
-		content, err := p.parseContent(body.Content, ctx)
+		content, err := p.parseContent(body.Content, body.Locator.Field("content"), ctx)
 		if err != nil {
 			return nil, errors.Wrap(err, "parse content")
 		}
diff --git a/openapi/parser/parse_response.go b/openapi/parser/parse_response.go
index 259d3460..30150d29 100644
--- a/openapi/parser/parse_response.go
+++ b/openapi/parser/parse_response.go
@@ -6,36 +6,35 @@ import (
 	"github.com/go-faster/errors"
 
 	"github.com/ogen-go/ogen"
+	"github.com/ogen-go/ogen/internal/location"
 	"github.com/ogen-go/ogen/openapi"
 )
 
-func (p *parser) parseResponses(responses ogen.Responses, ctx *resolveCtx) (_ map[string]*openapi.Response, err error) {
+func (p *parser) parseResponses(
+	responses ogen.Responses,
+	locator location.Locator,
+	ctx *resolveCtx,
+) (_ map[string]*openapi.Response, err error) {
 	if len(responses) == 0 {
 		return nil, errors.New("no responses")
 	}
 
 	result := make(map[string]*openapi.Response, len(responses))
 	for status, response := range responses {
-		result[status], err = p.parseStatus(status, response, ctx)
-		if err != nil {
-			return nil, errors.Wrap(err, status)
+		if err := validateStatusCode(status); err != nil {
+			return nil, p.wrapLocation(ctx.lastLoc(), locator.Key(status), err)
 		}
-	}
-
-	return result, nil
-}
 
-func (p *parser) parseStatus(status string, response *ogen.Response, ctx *resolveCtx) (*openapi.Response, error) {
-	if err := validateStatusCode(status); err != nil {
-		return nil, err
-	}
+		resp, err := p.parseResponse(response, ctx)
+		if err != nil {
+			err := errors.Wrap(err, status)
+			return nil, p.wrapLocation(ctx.lastLoc(), locator.Field(status), err)
+		}
 
-	resp, err := p.parseResponse(response, ctx)
-	if err != nil {
-		return nil, err
+		result[status] = resp
 	}
 
-	return resp, nil
+	return result, nil
 }
 
 func (p *parser) parseResponse(resp *ogen.Response, ctx *resolveCtx) (_ *openapi.Response, rerr error) {
@@ -54,7 +53,7 @@ func (p *parser) parseResponse(resp *ogen.Response, ctx *resolveCtx) (_ *openapi
 		return resp, nil
 	}
 
-	content, err := p.parseContent(resp.Content, ctx)
+	content, err := p.parseContent(resp.Content, resp.Locator.Field("content"), ctx)
 	if err != nil {
 		err := errors.Wrap(err, "content")
 		return nil, p.wrapField("content", ctx.lastLoc(), resp.Locator, err)
diff --git a/openapi/parser/parse_security.go b/openapi/parser/parse_security.go
index a6685966..4c9f01f7 100644
--- a/openapi/parser/parse_security.go
+++ b/openapi/parser/parse_security.go
@@ -7,6 +7,7 @@ import (
 	"github.com/go-faster/errors"
 
 	"github.com/ogen-go/ogen"
+	"github.com/ogen-go/ogen/internal/location"
 	"github.com/ogen-go/ogen/openapi"
 )
 
@@ -62,13 +63,14 @@ func (p *parser) parseSecurityScheme(
 				"vapid":
 			default:
 				err := errors.Errorf(`invalid "scheme": %q`, scheme.Scheme)
-				return p.wrapField("name", ctx.lastLoc(), scheme.Locator, err)
+				return p.wrapField("scheme", ctx.lastLoc(), scheme.Locator, err)
 			}
 			return nil
 		case "mutualTLS":
 			return nil
 		case "oauth2":
-			return p.validateOAuthFlows(scheme.Flows, ctx.lastLoc())
+			err := p.validateOAuthFlows(scheme.Flows, ctx.lastLoc())
+			return p.wrapField("flows", ctx.lastLoc(), scheme.Locator, err)
 		case "openIdConnect":
 			if _, err := url.ParseRequestURI(scheme.OpenIDConnectURL); err != nil {
 				err := errors.Wrap(err, `"openIdConnectUrl" MUST be in the form of a URL`)
@@ -127,7 +129,7 @@ func (p *parser) validateOAuthFlows(flows *ogen.OAuthFlows, loc string) (rerr er
 				return nil
 			}
 			if _, err := url.ParseRequestURI(input); err != nil {
-				err = errors.Wrapf(err, `%q MUST be in the form of a URL`, name)
+				err := errors.Wrapf(err, `%q MUST be in the form of a URL`, name)
 				return p.wrapField(name, loc, flow.Locator, err)
 			}
 			return nil
@@ -177,26 +179,37 @@ func cloneOAuthFlows(flows ogen.OAuthFlows) (r openapi.OAuthFlows) {
 
 func (p *parser) parseSecurityRequirements(
 	requirements ogen.SecurityRequirements,
+	locator location.Locator,
 	ctx *resolveCtx,
-) ([]openapi.SecurityRequirements, error) {
-	result := make([]openapi.SecurityRequirements, 0, len(requirements))
-	for _, req := range requirements {
-		for requirementName, scopes := range req {
-			v, ok := p.refs.securitySchemes[requirementName]
+) ([]openapi.SecurityRequirement, error) {
+	result := make([]openapi.SecurityRequirement, 0, len(requirements))
+	securitySchemesLoc := p.rootLoc.Field("components").Field("securitySchemes")
+
+	for idx, req := range requirements {
+		locator := locator.Index(idx)
+
+		for name, scopes := range req {
+			v, ok := p.refs.securitySchemes[name]
 			if !ok {
-				return nil, errors.Errorf("unknown security schema %q", requirementName)
+				err := errors.Errorf("unknown security schema %q", name)
+				return nil, p.wrapLocation(ctx.lastLoc(), locator.Key(name), err)
 			}
 
 			spec, err := p.parseSecurityScheme(v, ctx)
 			if err != nil {
-				return nil, errors.Wrapf(err, "parse security scheme %q", requirementName)
+				loc := securitySchemesLoc.Field(name)
+				err := errors.Wrapf(err, "parse security scheme %q", name)
+				return nil, p.wrapLocation(ctx.lastLoc(), loc, err)
 			}
 
 			if len(scopes) > 0 {
 				switch spec.Type {
 				case "openIdConnect", "oauth2":
 				default:
-					return nil, errors.Errorf(`list of scopes MUST be empty for "type" %q`, spec.Type)
+					// Point to the first scope in the list.
+					locator := locator.Field(name).Index(0)
+					err := errors.Errorf(`list of scopes MUST be empty for "type" %q`, spec.Type)
+					return nil, p.wrapLocation(ctx.lastLoc(), locator, err)
 				}
 			}
 
@@ -205,9 +218,9 @@ func (p *parser) parseSecurityRequirements(
 				flows = *spec.Flows
 			}
 
-			result = append(result, openapi.SecurityRequirements{
+			result = append(result, openapi.SecurityRequirement{
 				Scopes: scopes,
-				Name:   requirementName,
+				Name:   name,
 				Security: openapi.Security{
 					Type:             spec.Type,
 					Description:      spec.Description,
diff --git a/openapi/parser/parser.go b/openapi/parser/parser.go
index 4a306514..e96c0fcd 100644
--- a/openapi/parser/parser.go
+++ b/openapi/parser/parser.go
@@ -115,6 +115,8 @@ func (p *parser) parsePathItems() error {
 		//	names MUST NOT exist as they are identical.
 		//
 		paths = map[string]struct{}{}
+
+		pathsLoc = p.rootLoc.Field("paths")
 	)
 	for path, item := range p.spec.Paths {
 		if err := func() error {
@@ -124,15 +126,18 @@ func (p *parser) parsePathItems() error {
 			}
 
 			if _, ok := paths[id]; ok {
-				pathLoc := p.rootLoc.Field("paths").Key(path)
-				err := errors.Errorf("duplicate path: %q", path)
-				return p.wrapLocation("", pathLoc, err)
+				return errors.Errorf("duplicate path: %q", path)
 			}
 			paths[id] = struct{}{}
 
-			return p.parsePathItem(path, item, operationIDs, newResolveCtx(p.depthLimit))
+			return nil
 		}(); err != nil {
-			return errors.Wrapf(err, "path %q", path)
+			return p.wrapLocation("", pathsLoc.Key(path), err)
+		}
+
+		if err := p.parsePathItem(path, item, operationIDs, newResolveCtx(p.depthLimit)); err != nil {
+			err := errors.Wrapf(err, "path %q", path)
+			return p.wrapLocation("", pathsLoc.Field(path), err)
 		}
 	}
 	return nil
@@ -154,7 +159,7 @@ func (p *parser) parsePathItem(
 		return errors.New("referenced pathItem not supported")
 	}
 
-	itemParams, err := p.parseParams(item.Parameters, ctx)
+	itemParams, err := p.parseParams(item.Parameters, item.Locator.Field("parameters"), ctx)
 	if err != nil {
 		return errors.Wrap(err, "parameters")
 	}
@@ -199,7 +204,7 @@ func (p *parser) parseOp(
 		Locator:     spec.Locator,
 	}
 
-	opParams, err := p.parseParams(spec.Parameters, ctx)
+	opParams, err := p.parseParams(spec.Parameters, spec.Locator.Field("parameters"), ctx)
 	if err != nil {
 		return nil, errors.Wrap(err, "parameters")
 	}
@@ -219,26 +224,35 @@ func (p *parser) parseOp(
 		}
 	}
 
-	op.Responses, err = p.parseResponses(spec.Responses, ctx)
-	if err != nil {
-		return nil, errors.Wrap(err, "responses")
-	}
-
-	if spec.Security != nil {
-		// Use operation level security.
-		op.Security, err = p.parseSecurityRequirements(spec.Security, ctx)
+	{
+		locator := spec.Locator.Field("responses")
+		op.Responses, err = p.parseResponses(spec.Responses, locator, ctx)
 		if err != nil {
-			err = errors.Wrap(err, "security")
-			return nil, p.wrapField("security", ctx.lastLoc(), spec.Locator, err)
+			err := errors.Wrap(err, "responses")
+			return nil, p.wrapLocation(ctx.lastLoc(), locator, err)
 		}
-	} else {
-		// Use root level security.
-		op.Security, err = p.parseSecurityRequirements(p.spec.Security, ctx)
+	}
+
+	parseSecurity := func(spec ogen.SecurityRequirements, locator location.Locator) (err error) {
+		op.Security, err = p.parseSecurityRequirements(spec, locator, ctx)
 		if err != nil {
-			loc := p.rootLoc.Field("security")
-			err = errors.Wrap(err, "security")
-			return nil, p.wrapField("security", ctx.lastLoc(), loc, err)
+			err := errors.Wrap(err, "security")
+			return p.wrapLocation(ctx.lastLoc(), locator, err)
 		}
+		return nil
+	}
+
+	var (
+		security       = p.spec.Security
+		securityParent = p.rootLoc
+	)
+	if spec.Security != nil {
+		// Use operation level security.
+		security = spec.Security
+		securityParent = spec.Locator
+	}
+	if err := parseSecurity(security, securityParent.Field("security")); err != nil {
+		return nil, err
 	}
 
 	return op, nil
diff --git a/openapi/security.go b/openapi/security.go
index 9601dddc..b9c7adf0 100644
--- a/openapi/security.go
+++ b/openapi/security.go
@@ -2,8 +2,8 @@ package openapi
 
 import "github.com/ogen-go/ogen/internal/location"
 
-// SecurityRequirements is parsed security requirements.
-type SecurityRequirements struct {
+// SecurityRequirement is parsed security requirements.
+type SecurityRequirement struct {
 	Scopes   []string
 	Name     string
 	Security Security
diff --git a/schema.go b/schema.go
index c32f3a3d..21410bb7 100644
--- a/schema.go
+++ b/schema.go
@@ -73,6 +73,11 @@ type Schema struct {
 	// Discriminator for subschemas.
 	Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"`
 
+	// Adds additional metadata to describe the XML representation of this property.
+	//
+	// This MAY be used only on properties schemas. It has no effect on root schemas
+	XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"`
+
 	// The value of this keyword MUST be an array.
 	// This array SHOULD have at least one element.
 	// Elements in the array SHOULD be unique.
@@ -429,3 +434,45 @@ func (p *PatternProperties) UnmarshalJSON(data []byte) error {
 		return nil
 	})
 }
+
+// Discriminator discriminates types for OneOf, AllOf, AnyOf.
+//
+// See https://spec.openapis.org/oas/v3.1.0#discriminator-object.
+type Discriminator struct {
+	// REQUIRED. The name of the property in the payload that will hold the discriminator value.
+	PropertyName string `json:"propertyName" yaml:"propertyName"`
+	// An object to hold mappings between payload values and schema names or references.
+	Mapping map[string]string `json:"mapping,omitempty" yaml:"mapping,omitempty"`
+}
+
+// XML is a metadata object that allows for more fine-tuned XML model definitions.
+//
+// See https://spec.openapis.org/oas/v3.1.0#xml-object.
+type XML struct {
+	// Replaces the name of the element/attribute used for the described schema property.
+	//
+	// When defined within items, it will affect the name of the individual XML elements within the list.
+	//
+	// When defined alongside type being array (outside the items), it will affect the wrapping element
+	// and only if wrapped is true.
+	//
+	// If wrapped is false, it will be ignored.
+	Name string `json:"name,omitempty" yaml:"name,omitempty"`
+	// The URI of the namespace definition.
+	//
+	// This MUST be in the form of an absolute URI.
+	Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
+	// The prefix to be used for the name.
+	Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
+	// Declares whether the property definition translates to an attribute instead of an element.
+	//
+	// Default value is false.
+	Attribute bool `json:"attribute,omitempty" yaml:"attribute,omitempty"`
+	// MAY be used only for an array definition. Signifies whether the array is wrapped
+	// (for example, `<books><book/><book/></books>`) or unwrapped (`<book/><book/>`).
+	//
+	// The definition takes effect only when defined alongside type being array (outside the items).
+	//
+	// Default value is false.
+	Wrapped bool `json:"wrapped,omitempty" yaml:"wrapped,omitempty"`
+}
diff --git a/security_scheme.go b/security_scheme.go
index e4374199..b21c675b 100644
--- a/security_scheme.go
+++ b/security_scheme.go
@@ -1,6 +1,8 @@
 package ogen
 
 // SecurityScheme defines a security scheme that can be used by the operations.
+//
+// See https://spec.openapis.org/oas/v3.1.0#security-scheme-object.
 type SecurityScheme struct {
 	Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
 	// The type of the security scheme. Valid values are "apiKey", "http", "mutualTLS", "oauth2", "openIdConnect".
@@ -27,6 +29,8 @@ type SecurityScheme struct {
 }
 
 // OAuthFlows allows configuration of the supported OAuth Flows.
+//
+// See https://spec.openapis.org/oas/v3.1.0#oauth-flows-object.
 type OAuthFlows struct {
 	// Configuration for the OAuth Implicit flow.
 	Implicit *OAuthFlow `json:"implicit" yaml:"implicit"`
@@ -41,6 +45,8 @@ type OAuthFlows struct {
 }
 
 // OAuthFlow is configuration details for a supported OAuth Flow.
+//
+// See https://spec.openapis.org/oas/v3.1.0#oauth-flow-object.
 type OAuthFlow struct {
 	// The authorization URL to be used for this flow.
 	// This MUST be in the form of a URL. The OAuth2 standard requires the use of TLS.
@@ -59,4 +65,6 @@ type OAuthFlow struct {
 }
 
 // SecurityRequirements lists the required security schemes to execute this operation.
+//
+// See https://spec.openapis.org/oas/v3.1.0#security-requirement-object.
 type SecurityRequirements []map[string][]string
diff --git a/spec.go b/spec.go
index 3013df8b..4778d3a2 100644
--- a/spec.go
+++ b/spec.go
@@ -18,21 +18,48 @@ type (
 	Default = jsonschema.Default
 	// ExampleValue is an example value.
 	ExampleValue = jsonschema.Example
+	// RawValue is a raw JSON value.
+	RawValue = jsonschema.RawValue
 
 	// Locator stores location of JSON value.
 	Locator = location.Locator
 )
 
 // Spec is the root document object of the OpenAPI document.
+//
+// See https://spec.openapis.org/oas/v3.1.0#openapi-object.
 type Spec struct {
-	// This string MUST be the semantic version number
-	// of the OpenAPI Specification version that the OpenAPI document uses.
-	OpenAPI    string               `json:"openapi" yaml:"openapi"`
-	Info       Info                 `json:"info" yaml:"info"`
-	Servers    []Server             `json:"servers,omitempty" yaml:"servers,omitempty"`
-	Paths      Paths                `json:"paths,omitempty" yaml:"paths,omitempty"`
-	Components *Components          `json:"components,omitempty" yaml:"components,omitempty"`
-	Security   SecurityRequirements `json:"security,omitempty" yaml:"security,omitempty"`
+	// REQUIRED. This string MUST be the version number of the OpenAPI Specification
+	// that the OpenAPI document uses.
+	OpenAPI string `json:"openapi" yaml:"openapi"`
+	// REQUIRED. Provides metadata about the API.
+	//
+	// The metadata MAY be used by tooling as required.
+	Info Info `json:"info" yaml:"info"`
+	// The default value for the `$schema` keyword within Schema Objects contained within this OAS document.
+	JSONSchemaDialect string `json:"jsonSchemaDialect,omitempty" yaml:"jsonSchemaDialect,omitempty"`
+	// An array of Server Objects, which provide connectivity information to a target server.
+	Servers []Server `json:"servers,omitempty" yaml:"servers,omitempty"`
+	// The available paths and operations for the API.
+	Paths Paths `json:"paths,omitempty" yaml:"paths,omitempty"`
+	// The incoming webhooks that MAY be received as part of this API and that
+	// the API consumer MAY choose to implement.
+	//
+	// Closely related to the `callbacks` feature, this section describes requests initiated other
+	// than by an API call, for example by an out of band registration.
+	//
+	// The key name is a unique string to refer to each webhook, while the (optionally referenced)
+	// PathItem Object describes a request that may be initiated by the API provider and the expected responses.
+	Webhooks map[string]*PathItem `json:"webhooks,omitempty" yaml:"webhooks,omitempty"`
+	// An element to hold various schemas for the document.
+	Components *Components `json:"components,omitempty" yaml:"components,omitempty"`
+	// A declaration of which security mechanisms can be used across the API.
+	// The list of values includes alternative security requirement objects that can be used.
+	//
+	// Only one of the security requirement objects need to be satisfied to authorize a request.
+	//
+	// Individual operations can override this definition.
+	Security SecurityRequirements `json:"security,omitempty" yaml:"security,omitempty"`
 
 	// A list of tags used by the specification with additional metadata.
 	// The order of the tags can be used to reflect on their order by the parsing
@@ -89,32 +116,12 @@ func (s *Spec) Init() {
 	s.Components.Init()
 }
 
-// Example object.
-//
-// https://swagger.io/specification/#example-object
-type Example struct {
-	Ref           string       `json:"$ref,omitempty" yaml:"$ref,omitempty"` // ref object
-	Summary       string       `json:"summary,omitempty" yaml:"summary,omitempty"`
-	Description   string       `json:"description,omitempty" yaml:"description,omitempty"`
-	Value         ExampleValue `json:"value,omitempty" yaml:"value,omitempty"`
-	ExternalValue string       `json:"externalValue,omitempty" yaml:"externalValue,omitempty"`
-
-	Locator Locator `json:"-" yaml:",inline"`
-}
-
-// Tag object.
-//
-// https://swagger.io/specification/#tag-object
-type Tag struct {
-	Name         string                 `json:"name" yaml:"name"`
-	Description  string                 `json:"description,omitempty" yaml:"description,omitempty"`
-	ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
-}
-
 // Info provides metadata about the API.
 //
 // The metadata MAY be used by the clients if needed,
 // and MAY be presented in editing or documentation generation tools for convenience.
+//
+// See https://spec.openapis.org/oas/v3.1.0#info-object.
 type Info struct {
 	// REQUIRED. The title of the API.
 	Title string `json:"title" yaml:"title"`
@@ -134,19 +141,32 @@ type Info struct {
 }
 
 // Contact information for the exposed API.
+//
+// See https://spec.openapis.org/oas/v3.1.0#contact-object.
 type Contact struct {
-	Name  string `json:"name,omitempty" yaml:"name,omitempty"`
-	URL   string `json:"url,omitempty" yaml:"url,omitempty"`
+	// The identifying name of the contact person/organization.
+	Name string `json:"name,omitempty" yaml:"name,omitempty"`
+	// The URL pointing to the contact information.
+	URL string `json:"url,omitempty" yaml:"url,omitempty"`
+	// The email address of the contact person/organization.
 	Email string `json:"email,omitempty" yaml:"email,omitempty"`
 }
 
 // License information for the exposed API.
+//
+// See https://spec.openapis.org/oas/v3.1.0#license-object.
 type License struct {
-	Name string `json:"name,omitempty" yaml:"name,omitempty"`
-	URL  string `json:"url,omitempty" yaml:"url,omitempty"`
+	// REQUIRED. The license name used for the API.
+	Name string `json:"name" yaml:"name"`
+	// An SPDX license expression for the API.
+	Identifier string `json:"identifier,omitempty" yaml:"identifier,omitempty"`
+	// A URL to the license used for the API.
+	URL string `json:"url,omitempty" yaml:"url,omitempty"`
 }
 
 // Server represents a Server.
+//
+// See https://spec.openapis.org/oas/v3.1.0#server-object.
 type Server struct {
 	// REQUIRED. A URL to the target host. This URL supports Server Variables and MAY be relative,
 	// to indicate that the host location is relative to the location where the OpenAPI document is being served.
@@ -160,30 +180,26 @@ type Server struct {
 }
 
 // ServerVariable describes an object representing a Server Variable for server URL template substitution.
+//
+// See https://spec.openapis.org/oas/v3.1.0#server-variable-object
 type ServerVariable struct {
 	// An enumeration of string values to be used if the substitution options are from a limited set.
 	//
 	// The array MUST NOT be empty.
 	Enum []string `json:"enum,omitempty" yaml:"enum,omitempty"`
 	// REQUIRED. The default value to use for substitution, which SHALL be sent if an alternate value is not supplied.
-	// Note this behavior is different than the Schema Object’s treatment of default values, because in those
-	// cases parameter values are optional. If the enum is defined, the value MUST exist in the enum’s values.
+	// Note this behavior is different than the Schema Object's treatment of default values, because in those
+	// cases parameter values are optional. If the enum is defined, the value MUST exist in the enum's values.
 	Default string `json:"default" yaml:"default"`
 	// An optional description for the server variable. CommonMark syntax MAY be used for rich text representation.
 	Description string `json:"description,omitempty" yaml:"description,omitempty"`
 }
 
-// ExternalDocumentation describes a reference to external resource for extended documentation.
-type ExternalDocumentation struct {
-	// A description of the target documentation. CommonMark syntax MAY be used for rich text representation.
-	Description string `json:"description,omitempty" yaml:"description,omitempty"`
-	// REQUIRED. The URL for the target documentation. This MUST be in the form of a URL.
-	URL string `json:"url" yaml:"url"`
-}
-
-// Components hold a set of reusable objects for different aspects of the OAS.
-// All objects defined within the components object will have no effect on the API
-// unless they are explicitly referenced from properties outside the components object.
+// Components Holds a set of reusable objects for different aspects of the OAS.
+// All objects defined within the components object will have no effect on the API unless
+// they are explicitly referenced from properties outside the components object.
+//
+// See https://spec.openapis.org/oas/v3.1.0#components-object.
 type Components struct {
 	Schemas         map[string]*Schema         `json:"schemas,omitempty" yaml:"schemas,omitempty"`
 	Responses       map[string]*Response       `json:"responses,omitempty" yaml:"responses,omitempty"`
@@ -192,8 +208,8 @@ type Components struct {
 	RequestBodies   map[string]*RequestBody    `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"`
 	Headers         map[string]*Header         `json:"headers,omitempty" yaml:"headers,omitempty"`
 	SecuritySchemes map[string]*SecurityScheme `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"`
-	// Links           map[string]Link            `json:"links" yaml:"links"`
-	// Callbacks       map[string]Callback        `json:"callback" yaml:"callback"`
+	Links           map[string]*Link           `json:"links,omitempty" yaml:"links,omitempty"`
+	Callbacks       map[string]*Callback       `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
 
 	Locator Locator `json:"-" yaml:",inline"`
 }
@@ -229,107 +245,191 @@ func (c *Components) Init() {
 // Paths holds the relative paths to the individual endpoints and their operations.
 // The path is appended to the URL from the Server Object in order to construct the full URL.
 // The Paths MAY be empty, due to ACL constraints.
+//
+// See https://spec.openapis.org/oas/v3.1.0#paths-object.
 type Paths map[string]*PathItem
 
-// PathItem describes the operations available on a single path.
-// A Path Item MAY be empty, due to ACL constraints.
-// The path itself is still exposed to the documentation viewer,
-// but they will not know which operations and parameters are available.
+// PathItem Describes the operations available on a single path.
+// A Path Item MAY be empty, due to ACL constraints. The path itself is still exposed to the
+// documentation viewer, but they will not know which operations and parameters are available.
+//
+// See https://spec.openapis.org/oas/v3.1.0#path-item-object.
 type PathItem struct {
 	// Allows for an external definition of this path item.
 	// The referenced structure MUST be in the format of a Path Item Object.
 	// In case a Path Item Object field appears both
 	// in the defined object and the referenced object, the behavior is undefined.
-	Ref         string       `json:"$ref,omitempty" yaml:"$ref,omitempty"`
-	Summary     string       `json:"summary,omitempty" yaml:"summary,omitempty"`
-	Description string       `json:"description,omitempty" yaml:"description,omitempty"`
-	Get         *Operation   `json:"get,omitempty" yaml:"get,omitempty"`
-	Put         *Operation   `json:"put,omitempty" yaml:"put,omitempty"`
-	Post        *Operation   `json:"post,omitempty" yaml:"post,omitempty"`
-	Delete      *Operation   `json:"delete,omitempty" yaml:"delete,omitempty"`
-	Options     *Operation   `json:"options,omitempty" yaml:"options,omitempty"`
-	Head        *Operation   `json:"head,omitempty" yaml:"head,omitempty"`
-	Patch       *Operation   `json:"patch,omitempty" yaml:"patch,omitempty"`
-	Trace       *Operation   `json:"trace,omitempty" yaml:"trace,omitempty"`
-	Servers     []Server     `json:"servers,omitempty" yaml:"servers,omitempty"`
-	Parameters  []*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
+	Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
+
+	// An optional, string summary, intended to apply to all operations in this path.
+	Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
+	// An optional, string description, intended to apply to all operations in this path.
+	// CommonMark syntax MAY be used for rich text representation.
+	Description string `json:"description,omitempty" yaml:"description,omitempty"`
+	// A definition of a GET operation on this path.
+	Get *Operation `json:"get,omitempty" yaml:"get,omitempty"`
+	// A definition of a PUT operation on this path.
+	Put *Operation `json:"put,omitempty" yaml:"put,omitempty"`
+	// A definition of a POST operation on this path.
+	Post *Operation `json:"post,omitempty" yaml:"post,omitempty"`
+	// A definition of a DELETE operation on this path.
+	Delete *Operation `json:"delete,omitempty" yaml:"delete,omitempty"`
+	// A definition of a OPTIONS operation on this path.
+	Options *Operation `json:"options,omitempty" yaml:"options,omitempty"`
+	// A definition of a HEAD operation on this path.
+	Head *Operation `json:"head,omitempty" yaml:"head,omitempty"`
+	// A definition of a PATCH operation on this path.
+	Patch *Operation `json:"patch,omitempty" yaml:"patch,omitempty"`
+	// A definition of a TRACE operation on this path.
+	Trace *Operation `json:"trace,omitempty" yaml:"trace,omitempty"`
+	// An alternative server array to service all operations in this path.
+	Servers []Server `json:"servers,omitempty" yaml:"servers,omitempty"`
+	// A list of parameters that are applicable for all the operations described under this path.
+	//
+	// These parameters can be overridden at the operation level, but cannot be removed there.
+	//
+	// The list MUST NOT include duplicated parameters. A unique parameter is defined by
+	// a combination of a name and location.
+	Parameters []*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
 
 	Locator Locator `json:"-" yaml:",inline"`
 }
 
 // Operation describes a single API operation on a path.
+//
+// See https://spec.openapis.org/oas/v3.1.0#operation-object.
 type Operation struct {
-	OperationID string               `json:"operationId,omitempty" yaml:"operationId,omitempty"`
-	Security    SecurityRequirements `json:"security,omitempty" yaml:"security,omitempty"`
-	Parameters  []*Parameter         `json:"parameters,omitempty" yaml:"parameters,omitempty"`
-	RequestBody *RequestBody         `json:"requestBody,omitempty" yaml:"requestBody,omitempty"`
-	Responses   Responses            `json:"responses,omitempty" yaml:"responses,omitempty"`
-
 	// A list of tags for API documentation control.
 	// Tags can be used for logical grouping of operations by resources or any other qualifier.
-	Tags         []string               `json:"tags,omitempty" yaml:"tags,omitempty"`
-	Summary      string                 `json:"summary,omitempty" yaml:"summary,omitempty"`
-	Description  string                 `json:"description,omitempty" yaml:"description,omitempty"`
+	Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
+	// A short summary of what the operation does.
+	Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
+	// A verbose explanation of the operation behavior.
+	// CommonMark syntax MAY be used for rich text representation.
+	Description string `json:"description,omitempty" yaml:"description,omitempty"`
+	// Additional external documentation for this operation.
 	ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
-	Deprecated   bool                   `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
+
+	// Unique string used to identify the operation.
+	//
+	// The id MUST be unique among all operations described in the API.
+	//
+	// The operationId value is case-sensitive.
+	OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"`
+	// A list of parameters that are applicable for this operation.
+	//
+	// If a parameter is already defined at the Path Item, the new definition will override it but
+	// can never remove it.
+	//
+	// The list MUST NOT include duplicated parameters. A unique parameter is defined by
+	// a combination of a name and location.
+	Parameters []*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
+	// The request body applicable for this operation.
+	RequestBody *RequestBody `json:"requestBody,omitempty" yaml:"requestBody,omitempty"`
+	// The list of possible responses as they are returned from executing this operation.
+	Responses Responses `json:"responses,omitempty" yaml:"responses,omitempty"`
+	// A map of possible out-of band callbacks related to the parent operation.
+	//
+	// The key is a unique identifier for the Callback Object.
+	Callbacks map[string]*Callback `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
+	// Declares this operation to be deprecated
+	Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
+	// A declaration of which security mechanisms can be used for this operation.
+	//
+	// The list of values includes alternative security requirement objects that can be used.
+	//
+	// Only one of the security requirement objects need to be satisfied to authorize a request.
+	Security SecurityRequirements `json:"security,omitempty" yaml:"security,omitempty"`
+	// An alternative server array to service this operation.
+	//
+	// If an alternative server object is specified at the Path Item Object or Root level,
+	// it will be overridden by this value.
+	Servers []Server `json:"servers,omitempty" yaml:"servers,omitempty"`
 
 	Locator Locator `json:"-" yaml:",inline"`
 }
 
+// ExternalDocumentation describes a reference to external resource for extended documentation.
+//
+// See https://spec.openapis.org/oas/v3.1.0#external-documentation-object.
+type ExternalDocumentation struct {
+	// A description of the target documentation. CommonMark syntax MAY be used for rich text representation.
+	Description string `json:"description,omitempty" yaml:"description,omitempty"`
+	// REQUIRED. The URL for the target documentation. This MUST be in the form of a URL.
+	URL string `json:"url" yaml:"url"`
+}
+
 // Parameter describes a single operation parameter.
 // A unique parameter is defined by a combination of a name and location.
+//
+// See https://spec.openapis.org/oas/v3.1.0#parameter-object.
 type Parameter struct {
-	Ref  string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
-	Name string `json:"name" yaml:"name"`
-
-	// The location of the parameter. Possible values are "query", "header", "path" or "cookie".
-	In          string  `json:"in" yaml:"in"`
-	Description string  `json:"description,omitempty" yaml:"description,omitempty"`
-	Schema      *Schema `json:"schema,omitempty" yaml:"schema,omitempty"`
+	Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` // ref object
 
+	// REQUIRED. The name of the parameter. Parameter names are case sensitive.
+	Name string `json:"name" yaml:"name"`
+	// REQUIRED. The location of the parameter. Possible values are "query", "header", "path" or "cookie".
+	In string `json:"in" yaml:"in"`
+	// A brief description of the parameter. This could contain examples of use.
+	// CommonMark syntax MAY be used for rich text representation.
+	Description string `json:"description,omitempty" yaml:"description,omitempty"`
 	// Determines whether this parameter is mandatory.
 	// If the parameter location is "path", this property is REQUIRED
 	// and its value MUST be true.
 	// Otherwise, the property MAY be included and its default value is false.
 	Required bool `json:"required,omitempty" yaml:"required,omitempty"`
-
 	// Specifies that a parameter is deprecated and SHOULD be transitioned out of usage.
 	// Default value is false.
 	Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
 
-	// For more complex scenarios, the content property can define the media type and schema of the parameter.
-	// A parameter MUST contain either a schema property, or a content property, but not both.
-	// When example or examples are provided in conjunction with the schema object,
-	// the example MUST follow the prescribed serialization strategy for the parameter.
-	//
-	// A map containing the representations for the parameter.
-	// The key is the media type and the value describes it.
-	// The map MUST only contain one entry.
-	Content map[string]Media `json:"content,omitempty" yaml:"content,omitempty"`
-
 	// Describes how the parameter value will be serialized
 	// depending on the type of the parameter value.
 	Style string `json:"style,omitempty" yaml:"style,omitempty"`
-
 	// When this is true, parameter values of type array or object
 	// generate separate parameters for each value of the array
 	// or key-value pair of the map.
 	// For other types of parameters this property has no effect.
 	Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"`
-
-	Example  ExampleValue        `json:"example,omitempty" yaml:"example,omitempty"`
+	// Determines whether the parameter value SHOULD allow reserved characters, as defined by RFC 3986.
+	//
+	// This property only applies to parameters with an in value of query.
+	//
+	// The default value is false.
+	AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
+	// The schema defining the type used for the parameter.
+	Schema *Schema `json:"schema,omitempty" yaml:"schema,omitempty"`
+	// Example of the parameter's potential value.
+	Example ExampleValue `json:"example,omitempty" yaml:"example,omitempty"`
+	// Examples of the parameter's potential value.
 	Examples map[string]*Example `json:"examples,omitempty" yaml:"examples,omitempty"`
 
+	// For more complex scenarios, the content property can define the media type and schema of the parameter.
+	// A parameter MUST contain either a schema property, or a content property, but not both.
+	// When example or examples are provided in conjunction with the schema object,
+	// the example MUST follow the prescribed serialization strategy for the parameter.
+	//
+	// A map containing the representations for the parameter.
+	// The key is the media type and the value describes it.
+	// The map MUST only contain one entry.
+	Content map[string]Media `json:"content,omitempty" yaml:"content,omitempty"`
+
 	Locator Locator `json:"-" yaml:",inline"`
 }
 
 // RequestBody describes a single request body.
+//
+// See https://spec.openapis.org/oas/v3.1.0#request-body-object.
 type RequestBody struct {
-	Ref         string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
+	Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` // ref object
+
+	// A brief description of the request body. This could contain examples of use.
+	// CommonMark syntax MAY be used for rich text representation.
 	Description string `json:"description,omitempty" yaml:"description,omitempty"`
 
-	// The content of the request body.
+	// REQUIRED. The content of the request body.
+	//
 	// The key is a media type or media type range and the value describes it.
+	//
 	// For requests that match multiple keys, only the most specific key is applicable.
 	// e.g. text/plain overrides text/*
 	Content map[string]Media `json:"content,omitempty" yaml:"content,omitempty"`
@@ -340,41 +440,22 @@ type RequestBody struct {
 	Locator Locator `json:"-" yaml:",inline"`
 }
 
-// Responses is a container for the expected responses of an operation.
-// The container maps the HTTP response code to the expected response
-type Responses map[string]*Response
-
-// Response describes a single response from an API Operation,
-// including design-time, static links to operations based on the response.
-type Response struct {
-	Ref         string                 `json:"$ref,omitempty" yaml:"$ref,omitempty"`
-	Description string                 `json:"description,omitempty" yaml:"description,omitempty"`
-	Headers     map[string]*Header     `json:"headers,omitempty" yaml:"headers,omitempty"`
-	Content     map[string]Media       `json:"content,omitempty" yaml:"content,omitempty"`
-	Links       map[string]interface{} `json:"links,omitempty" yaml:"links,omitempty"` // TODO: implement
-
-	Locator Locator `json:"-" yaml:",inline"`
-}
-
-// Header describes header response.
-//
-// Header Object follows the structure of the Parameter Object with the following changes:
-//
-// 	1. `name` MUST NOT be specified, it is given in the corresponding headers map.
-// 	2. `in` MUST NOT be specified, it is implicitly in header.
-// 	3. All traits that are affected by the location MUST be applicable to a location of header.
-//
-type Header = Parameter
-
 // Media provides schema and examples for the media type identified by its key.
+//
+// See https://spec.openapis.org/oas/v3.1.0#media-type-object.
 type Media struct {
 	// The schema defining the content of the request, response, or parameter.
-	Schema   *Schema             `json:"schema,omitempty" yaml:"schema,omitempty"`
-	Example  ExampleValue        `json:"example,omitempty" yaml:"example,omitempty"`
+	Schema *Schema `json:"schema,omitempty" yaml:"schema,omitempty"`
+	// Example of the media type.
+	Example ExampleValue `json:"example,omitempty" yaml:"example,omitempty"`
+	// Examples of the media type.
 	Examples map[string]*Example `json:"examples,omitempty" yaml:"examples,omitempty"`
 
-	// A map between a property name and its encoding information. The key, being the property name, MUST exist in
-	// the schema as a property. The encoding object SHALL only apply to requestBody objects when the media
+	// A map between a property name and its encoding information.
+	//
+	// The key, being the property name, MUST exist in the schema as a property.
+	//
+	// The encoding object SHALL only apply to requestBody objects when the media
 	// type is multipart or application/x-www-form-urlencoded.
 	Encoding map[string]Encoding `json:"encoding,omitempty" yaml:"encoding,omitempty"`
 
@@ -382,6 +463,8 @@ type Media struct {
 }
 
 // Encoding describes single encoding definition applied to a single schema property.
+//
+// See https://spec.openapis.org/oas/v3.1.0#encoding-object.
 type Encoding struct {
 	// The Content-Type for encoding a specific property.
 	ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"`
@@ -410,8 +493,125 @@ type Encoding struct {
 	Locator Locator `json:"-" yaml:",inline"`
 }
 
-// Discriminator discriminates types for OneOf, AllOf, AnyOf.
-type Discriminator struct {
-	PropertyName string            `json:"propertyName" yaml:"propertyName"`
-	Mapping      map[string]string `json:"mapping,omitempty" yaml:"mapping,omitempty"`
+// Responses is a container for the expected responses of an operation.
+//
+// The container maps the HTTP response code to the expected response.
+//
+// The `default` MAY be used as a default response object for all HTTP
+// codes that are not covered individually by the Responses Object.
+//
+// The Responses Object MUST contain at least one response code, and if only one
+// response code is provided it SHOULD be the response for a successful operation call.
+//
+// See https://spec.openapis.org/oas/v3.1.0#responses-object.
+type Responses map[string]*Response
+
+// Response describes a single response from an API Operation,
+// including design-time, static links to operations based on the response.
+//
+// See https://spec.openapis.org/oas/v3.1.0#response-object.
+type Response struct {
+	Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` // ref object
+
+	// REQUIRED. A description of the response.
+	// CommonMark syntax MAY be used for rich text representation.
+	Description string `json:"description,omitempty" yaml:"description,omitempty"`
+	// Maps a header name to its definition.
+	//
+	// RFC7230 states header names are case insensitive.
+	//
+	// If a response header is defined with the name "Content-Type", it SHALL be ignored.
+	Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"`
+	// A map containing descriptions of potential response payloads.
+	//
+	// The key is a media type or media type range and the value describes it.
+	//
+	// For requests that match multiple keys, only the most specific key is applicable.
+	// e.g. text/plain overrides text/*
+	Content map[string]Media `json:"content,omitempty" yaml:"content,omitempty"`
+	// A map of operations links that can be followed from the response.
+	//
+	// The key of the map is a short name for the link, following the naming constraints
+	// of the names for Component Objects.
+	Links map[string]*Link `json:"links,omitempty" yaml:"links,omitempty"`
+
+	Locator Locator `json:"-" yaml:",inline"`
+}
+
+// Callback is a map of possible out-of band callbacks related to the parent operation.
+//
+// Each value in the map is a Path Item Object that describes a set of requests that may be
+// initiated by the API provider and the expected responses.
+//
+// The key value used to identify the path item object is an expression, evaluated at runtime,
+// that identifies a URL to use for the callback operation.
+//
+// To describe incoming requests from the API provider independent from another
+// API call, use the `webhooks` field.
+//
+// See https://spec.openapis.org/oas/v3.1.0#callback-object.
+type Callback map[string]*PathItem
+
+// Example object.
+//
+// See https://spec.openapis.org/oas/v3.1.0#example-object.
+type Example struct {
+	Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` // ref object
+
+	// Short description for the example.
+	Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
+	// Long description for the example.
+	// CommonMark syntax MAY be used for rich text representation.
+	Description string `json:"description,omitempty" yaml:"description,omitempty"`
+	// Embedded literal example.
+	Value ExampleValue `json:"value,omitempty" yaml:"value,omitempty"`
+	// A URI that points to the literal example.
+	ExternalValue string `json:"externalValue,omitempty" yaml:"externalValue,omitempty"`
+
+	Locator Locator `json:"-" yaml:",inline"`
+}
+
+// Link describes a possible design-time link for a response.
+//
+// See https://spec.openapis.org/oas/v3.1.0#link-object.
+type Link struct {
+	Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` // ref object
+
+	// A relative or absolute URI reference to an OAS operation.
+	//
+	// This field is mutually exclusive of the operationId field, and MUST point to an Operation Object.
+	OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"`
+	// The name of an existing, resolvable OAS operation, as defined with a unique operationId.
+	//
+	// This field is mutually exclusive of the operationRef field.
+	OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"`
+	// A map representing parameters to pass to an operation as specified with operationId or identified
+	// via operationRef.
+	//
+	// The key is the parameter name to be used, whereas the value can be a constant or an expression to be
+	// evaluated and passed to the linked operation.
+	Parameters map[string]RawValue `json:"parameters,omitempty" yaml:"parameters,omitempty"`
+}
+
+// Header describes header response.
+//
+// Header Object follows the structure of the Parameter Object with the following changes:
+//
+// 	1. `name` MUST NOT be specified, it is given in the corresponding headers map.
+// 	2. `in` MUST NOT be specified, it is implicitly in header.
+// 	3. All traits that are affected by the location MUST be applicable to a location of header.
+//
+// See https://spec.openapis.org/oas/v3.1.0#header-object.
+type Header = Parameter
+
+// Tag adds metadata to a single tag that is used by the Operation Object.
+//
+// See https://spec.openapis.org/oas/v3.1.0#tag-object
+type Tag struct {
+	// REQUIRED. The name of the tag.
+	Name string `json:"name" yaml:"name"`
+	// A description for the tag. CommonMark syntax MAY be used for rich text representation.
+	Description string `json:"description,omitempty" yaml:"description,omitempty"`
+	// Additional external documentation for this tag.
+	ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
 }
diff --git a/spec_backcomp.go b/spec_backcomp.go
index 608ab0e6..99823be3 100644
--- a/spec_backcomp.go
+++ b/spec_backcomp.go
@@ -35,7 +35,6 @@ func (s *Schema) ToJSONSchema() *jsonschema.RawSchema {
 		AllOf:                convertMany(s.AllOf),
 		OneOf:                convertMany(s.OneOf),
 		AnyOf:                convertMany(s.AnyOf),
-		Discriminator:        s.Discriminator.ToJSONSchema(),
 		Enum:                 s.Enum,
 		MultipleOf:           s.MultipleOf,
 		Maximum:              s.Maximum,
@@ -51,10 +50,12 @@ func (s *Schema) ToJSONSchema() *jsonschema.RawSchema {
 		MaxProperties:        s.MaxProperties,
 		MinProperties:        s.MinProperties,
 		Default:              s.Default,
-		Example:              s.Example,
 		Deprecated:           s.Deprecated,
 		ContentEncoding:      s.ContentEncoding,
 		ContentMediaType:     s.ContentMediaType,
+		Discriminator:        s.Discriminator.ToJSONSchema(),
+		XML:                  s.XML.ToJSONSchema(),
+		Example:              s.Example,
 		XAnnotations:         map[string]json.RawMessage{},
 		Locator:              s.Locator,
 	}
@@ -116,3 +117,18 @@ func (d *Discriminator) ToJSONSchema() *jsonschema.Discriminator {
 		Mapping:      d.Mapping,
 	}
 }
+
+// ToJSONSchema converts XML to jsonschema.XML.
+func (d *XML) ToJSONSchema() *jsonschema.XML {
+	if d == nil {
+		return nil
+	}
+
+	return &jsonschema.XML{
+		Name:      d.Name,
+		Namespace: d.Namespace,
+		Prefix:    d.Prefix,
+		Attribute: d.Attribute,
+		Wrapped:   d.Wrapped,
+	}
+}


Test patch:
diff --git a/_testdata/negative/headers/header_empty_content.json b/_testdata/negative/headers/header_empty_content.json
new file mode 100644
index 00000000..c65dd5ea
--- /dev/null
+++ b/_testdata/negative/headers/header_empty_content.json
@@ -0,0 +1,24 @@
+{
+  "openapi": "3.0.3",
+  "info": {
+    "title": "title",
+    "version": "v0.1.0"
+  },
+  "paths": {
+    "/foo": {
+      "get": {
+        "responses": {
+          "200": {
+            "headers": {
+              "foo": {
+                "content": {
+                }
+              }
+            },
+            "description": "User info"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/_testdata/negative/headers/header_multiple_content_entries.json b/_testdata/negative/headers/header_multiple_content_entries.json
new file mode 100644
index 00000000..bcfdac16
--- /dev/null
+++ b/_testdata/negative/headers/header_multiple_content_entries.json
@@ -0,0 +1,34 @@
+{
+  "openapi": "3.0.3",
+  "info": {
+    "title": "title",
+    "version": "v0.1.0"
+  },
+  "paths": {
+    "/foo": {
+      "get": {
+        "responses": {
+          "200": {
+            "headers": {
+              "foo": {
+                "content": {
+                  "application/json": {
+                    "schema": {
+                      "type": "string"
+                    }
+                  },
+                  "text/plain": {
+                    "schema": {
+                      "type": "string"
+                    }
+                  }
+                }
+              }
+            },
+            "description": "User info"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/_testdata/negative/headers/header_with_content_and_schema.json b/_testdata/negative/headers/header_with_content_and_schema.json
new file mode 100644
index 00000000..945a4604
--- /dev/null
+++ b/_testdata/negative/headers/header_with_content_and_schema.json
@@ -0,0 +1,32 @@
+{
+  "openapi": "3.0.3",
+  "info": {
+    "title": "title",
+    "version": "v0.1.0"
+  },
+  "paths": {
+    "/foo": {
+      "get": {
+        "responses": {
+          "200": {
+            "headers": {
+              "foo": {
+                "schema": {
+                  "type": "string"
+                },
+                "content": {
+                  "application/json": {
+                    "schema": {
+                      "type": "string"
+                    }
+                  }
+                }
+              }
+            },
+            "description": "User info"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/_testdata/negative/headers/invalid_content_type.json b/_testdata/negative/headers/invalid_content_type.json
new file mode 100644
index 00000000..ed84fefe
--- /dev/null
+++ b/_testdata/negative/headers/invalid_content_type.json
@@ -0,0 +1,29 @@
+{
+  "openapi": "3.0.3",
+  "info": {
+    "title": "title",
+    "version": "v0.1.0"
+  },
+  "paths": {
+    "/foo": {
+      "get": {
+        "responses": {
+          "200": {
+            "headers": {
+              "foo": {
+                "content": {
+                  ";": {
+                    "schema": {
+                      "type": "string"
+                    }
+                  }
+                }
+              }
+            },
+            "description": "User info"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/_testdata/negative/null/header.json b/_testdata/negative/null/components_header.json
similarity index 100%
rename from _testdata/negative/null/header.json
rename to _testdata/negative/null/components_header.json
diff --git a/_testdata/negative/null/securityScheme.json b/_testdata/negative/null/components_securityScheme.json
similarity index 100%
rename from _testdata/negative/null/securityScheme.json
rename to _testdata/negative/null/components_securityScheme.json
diff --git a/_testdata/negative/null/parameter.json b/_testdata/negative/null/operation_parameter.json
similarity index 62%
rename from _testdata/negative/null/parameter.json
rename to _testdata/negative/null/operation_parameter.json
index 168a8673..43a23867 100644
--- a/_testdata/negative/null/parameter.json
+++ b/_testdata/negative/null/operation_parameter.json
@@ -9,7 +9,12 @@
       "get": {
         "parameters": [
           null
-        ]
+        ],
+        "responses": {
+          "200": {
+            "description": "User info"
+          }
+        }
       }
     }
   }
diff --git a/_testdata/negative/null/path_item.json b/_testdata/negative/null/pathItem.json
similarity index 100%
rename from _testdata/negative/null/path_item.json
rename to _testdata/negative/null/pathItem.json
diff --git a/_testdata/negative/null/pathItem_parameter.json b/_testdata/negative/null/pathItem_parameter.json
new file mode 100644
index 00000000..74d146c1
--- /dev/null
+++ b/_testdata/negative/null/pathItem_parameter.json
@@ -0,0 +1,21 @@
+{
+  "openapi": "3.0.3",
+  "info": {
+    "title": "title",
+    "version": "v0.1.0"
+  },
+  "paths": {
+    "/foo": {
+      "parameters": [
+        null
+      ],
+      "get": {
+        "responses": {
+          "200": {
+            "description": "User info"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/_testdata/negative/parameters/invalid_content_type.json b/_testdata/negative/parameters/invalid_content_type.json
new file mode 100644
index 00000000..75a90677
--- /dev/null
+++ b/_testdata/negative/parameters/invalid_content_type.json
@@ -0,0 +1,31 @@
+{
+  "openapi": "3.0.3",
+  "info": {
+    "title": "title",
+    "version": "v0.1.0"
+  },
+  "paths": {
+    "/foo": {
+      "get": {
+        "parameters": [
+          {
+            "name": "foo",
+            "in": "query",
+            "content": {
+              ";": {
+                "schema": {
+                  "type": "string"
+                }
+              }
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "User info"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/_testdata/negative/parameters/parameter_multiple_content_entries.json b/_testdata/negative/parameters/parameter_multiple_content_entries.json
new file mode 100644
index 00000000..c2842b10
--- /dev/null
+++ b/_testdata/negative/parameters/parameter_multiple_content_entries.json
@@ -0,0 +1,36 @@
+{
+  "openapi": "3.0.3",
+  "info": {
+    "title": "title",
+    "version": "v0.1.0"
+  },
+  "paths": {
+    "/foo": {
+      "get": {
+        "parameters": [
+          {
+            "name": "foo",
+            "in": "query",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "string"
+                }
+              },
+              "text/plain": {
+                "schema": {
+                  "type": "string"
+                }
+              }
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "User info"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/_testdata/negative/paths/invalid_path.json b/_testdata/negative/paths/invalid_path.json
new file mode 100644
index 00000000..0328a175
--- /dev/null
+++ b/_testdata/negative/paths/invalid_path.json
@@ -0,0 +1,18 @@
+{
+  "openapi": "3.0.3",
+  "info": {
+    "title": "title",
+    "version": "v0.1.0"
+  },
+  "paths": {
+    "/foo/{param": {
+      "get": {
+        "responses": {
+          "200": {
+            "description": "User info"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/_testdata/negative/requestBody/invalid_content_type.json b/_testdata/negative/requestBody/invalid_content_type.json
new file mode 100644
index 00000000..2a1a57a3
--- /dev/null
+++ b/_testdata/negative/requestBody/invalid_content_type.json
@@ -0,0 +1,27 @@
+{
+  "openapi": "3.0.3",
+  "info": {
+    "title": "title",
+    "version": "v0.1.0"
+  },
+  "paths": {
+    "/foo": {
+      "post": {
+        "requestBody": {
+          "content": {
+            ";": {
+              "schema": {
+                "type": "string"
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "User info"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/_testdata/negative/responses/invalid_content_type.json b/_testdata/negative/responses/invalid_content_type.json
new file mode 100644
index 00000000..adaf04c7
--- /dev/null
+++ b/_testdata/negative/responses/invalid_content_type.json
@@ -0,0 +1,25 @@
+{
+  "openapi": "3.0.3",
+  "info": {
+    "title": "title",
+    "version": "v0.1.0"
+  },
+  "paths": {
+    "/foo": {
+      "post": {
+        "responses": {
+          "200": {
+            "description": "User info",
+            "content": {
+              ";": {
+                "schema": {
+                  "type": "string"
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/_testdata/negative/responses/invalid_status_code.json b/_testdata/negative/responses/invalid_status_code.json
new file mode 100644
index 00000000..fb77c1c3
--- /dev/null
+++ b/_testdata/negative/responses/invalid_status_code.json
@@ -0,0 +1,18 @@
+{
+  "openapi": "3.0.3",
+  "info": {
+    "title": "title",
+    "version": "v0.1.0"
+  },
+  "paths": {
+    "/foo": {
+      "get": {
+        "responses": {
+          "XX": {
+            "description": "User info"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/_testdata/negative/responses/invalid_http_code.json b/_testdata/negative/responses/unknown_status_code.json
similarity index 100%
rename from _testdata/negative/responses/invalid_http_code.json
rename to _testdata/negative/responses/unknown_status_code.json
diff --git a/_testdata/positive/format_gen.json b/_testdata/positive/format_gen.json
index e23b29bb..a99bde96 100644
--- a/_testdata/positive/format_gen.json
+++ b/_testdata/positive/format_gen.json
@@ -13,645 +13,645 @@
 					{
 						"name": "boolean",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "boolean"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "boolean_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "boolean"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "integer",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "integer"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "integer_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "integer"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "integer_int32",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "integer",
 							"format": "int32"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "integer_int32_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "integer",
 								"format": "int32"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "integer_int64",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "integer",
 							"format": "int64"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "integer_int64_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "integer",
 								"format": "int64"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "number",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "number"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "number_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "number"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "number_double",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "number",
 							"format": "double"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "number_double_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "number",
 								"format": "double"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "number_float",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "number",
 							"format": "float"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "number_float_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "number",
 								"format": "float"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "number_int32",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "number",
 							"format": "int32"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "number_int32_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "number",
 								"format": "int32"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "number_int64",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "number",
 							"format": "int64"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "number_int64_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "number",
 								"format": "int64"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_binary",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "binary"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_binary_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "binary"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_byte",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "byte"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_byte_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "byte"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_date",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "date"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_date-time",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "date-time"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_date-time_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "date-time"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_date_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "date"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_duration",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "duration"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_duration_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "duration"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_email",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "email"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_email_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "email"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_hostname",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "hostname"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_hostname_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "hostname"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_int32",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "int32"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_int32_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "int32"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_int64",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "int64"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_int64_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "int64"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_ip",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "ip"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_ip_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "ip"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_ipv4",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "ipv4"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_ipv4_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "ipv4"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_ipv6",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "ipv6"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_ipv6_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "ipv6"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_password",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "password"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_password_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "password"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_time",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "time"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_time_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "time"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_unix",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "unix"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_unix-micro",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "unix-micro"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_unix-micro_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "unix-micro"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_unix-milli",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "unix-milli"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_unix-milli_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "unix-milli"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_unix-nano",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "unix-nano"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_unix-nano_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "unix-nano"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_unix-seconds",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "unix-seconds"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_unix-seconds_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "unix-seconds"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_unix_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "unix"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_uri",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "uri"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_uri_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "uri"
 							}
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_uuid",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "string",
 							"format": "uuid"
-						},
-						"required": true
+						}
 					},
 					{
 						"name": "string_uuid_array",
 						"in": "query",
+						"required": true,
 						"schema": {
 							"type": "array",
 							"items": {
 								"type": "string",
 								"format": "uuid"
 							}
-						},
-						"required": true
+						}
 					}
 				],
 				"requestBody": {
diff --git a/openapi/parser/resolve_external_test.go b/openapi/parser/resolve_external_test.go
index c31968f6..96911655 100644
--- a/openapi/parser/resolve_external_test.go
+++ b/openapi/parser/resolve_external_test.go
@@ -250,7 +250,7 @@ func TestExternalReference(t *testing.T) {
 				},
 				Parameters:  []*openapi.Parameter{param},
 				RequestBody: requestBody,
-				Security:    []openapi.SecurityRequirements{},
+				Security:    []openapi.SecurityRequirement{},
 				Responses: map[string]*openapi.Response{
 					"200": response,
 				},
diff --git a/openapi/parser/resolve_test.go b/openapi/parser/resolve_test.go
index 4c77d63b..d939e50c 100644
--- a/openapi/parser/resolve_test.go
+++ b/openapi/parser/resolve_test.go
@@ -150,7 +150,7 @@ func TestComplicatedReference(t *testing.T) {
 					},
 				},
 			},
-			Security: []openapi.SecurityRequirements{},
+			Security: []openapi.SecurityRequirement{},
 			Responses: map[string]*openapi.Response{
 				"200": {
 					Ref: "#/paths/~1post/post/responses/200",
@@ -211,7 +211,7 @@ func TestComplicatedReference(t *testing.T) {
 					},
 				},
 			},
-			Security: []openapi.SecurityRequirements{},
+			Security: []openapi.SecurityRequirement{},
 			Responses: map[string]*openapi.Response{
 				"200": {
 					Headers: map[string]*openapi.Header{