Swagger schema generation rules

Schema generation rules #

Lots of the work carried out by go-swagger is to generate models, which can have all kinds of rules like polymorphism and validations. Go-swagger models are the go data structures used for serialization and validation.

Of course none of this is possible without a set of rules and trade-offs.

About schemas #

A schema is a data structure specified in a Swagger document. Loosely speaking, a swagger schema corresponds to a JSONSchema-draft4 schema (see differences below). For each schema, go-swagger will generate one or more model types in go.

NOTE: Swagger makes a distinction between schemas and “simple schemas”. We use simple schemas to describe parameters and headers for operations. Simple schemas are supported by go-swagger (including validation), but are not rendered as standalone models. Rather, simple schema structures and validation methods are generated with the operation they are attached to.

Models are dedicated to the description of Swagger schemas, which are described as a parameter body, a response or a spec definition.

Interfaces #

The generated models implements:

  • a serialization interface
    • MarshalJSON(), UnmarshalJSON():
      • standard structures use JSON decoder tags (serialization tags are customizable)
      • composed and extensible structures use custom marshalers (allOf, additionalProperties, tuples and polymorphic types)
    • MarshalBinary(), UnmarshalBinary() interfaces (encoding/BinaryMarshaler, encoding/BinaryUnmarshaler), which may use the fast mailru/easyjson package
  • a validation interface (go-openapi/runtime/Validatable), with a Validate(strfmt.Registry) error method

Validation methods are wired at generation time, and rely mostly on native types: this makes validation faster than a dynamic general purpose JSON schema validator.

Example of a generated structure:

// Principal principal
// swagger:model principal
type Principal struct {

	// name
	Name string `json:"name,omitempty"`

	// roles
	Roles []string `json:"roles"`
}

NOTE: if you are looking for a dynamic, fully JSONSchema compliant, general purpose validator, the go-openapi/validate package is what you want. It is fully tested against the JSONSchema-Test-Suite (supports JSON-schema-draft4). See a working example here.

Mapping patterns #

The general idea is that you should rarely see interface{} in the generated code: you get a complete representation of a swagger document in somewhat idiomatic go.

There is set of mapping patterns that are applied to transform a spec into go types:

  • definition of primitive => type alias/name
  • definition of array => type alias/name
  • definition of map => type alias/name
  • definition of object with properties => struct
  • definition of $ref => type alias/name
  • object with only additional properties => map[string]T
  • object with additional properties and properties => custom serializer
  • schema with schema array in items => tuple (struct with properties, custom serializer)
  • schema with all of => struct
  • all of schema with ref => embedded value
  • all of schema with properties => properties are included in struct
  • adding an all of schema with just x-isnullable: true or x-nullable: true turns the schema into a pointer when there are only other extension properties provided
  • additional items in tuples => JSONSchema and by extension swagger allow for items that have a fixed size array with schema’s describing the items at each index. This can be combined with additional items to form some kind of tuple with varargs. To map this to go it creates a struct that has fixed names and a custom json serializer.

Minimal use of go’s reflection #

Generated models uses no reflection except for enum and required validations. This makes validation faster.

Doc strings #

All documentation items provided by the spec are integrated as godoc-friendly comments in the generated source. You may look at how a trivial example is rendered here.

The code that is generated also gets the same doc comments that are used by the scanner to generate a spec from go code (e.g. comments like // swagger:xxx). So that after generation you should be able to reverse-generate a spec from the code that was generated by your spec. It should be equivalent to the original spec but might miss some default values and examples.

Types reusability #

Models can be generated independently from other components of your API. Internal model structures may thus be safely regenerated if the contract at your endpoints does not change.

Reusing previous generations #

Previously generated models can be reused when constructing a new API server or client (e.g. using swagger generate server --model=[my existing package]).

The generator makes every effort to keep the go code readable, idiomatic and commented: models may thus be manually customized or extended. Such customized types may be later on reused in other specs, using the x-go-type extension.

Swagger vs JSONSchema #

A Swagger 2.0 schema corresponds by and large to a JSON-schema-draft4. However, there are some substantial differences (see swagger):

In JSONSchema, but not in Swagger 2.0:

  • anyOf, oneOf and not constructs are not supported (this is for OpenAPI 3)
  • the null type is not supported (the nullable keyword is defined in OpenAPI 3)
  • additionalItems are not supported (go-swagger does support it)
  • patternProperties are not supported
  • dependencies are not supported
  • multiple types, defined as type: [ ... ] are not supported

Conversely, what we have in Swagger 2.0, but not in JSON-schema:

  • the discriminator attribute controls polymorphism (see below)
  • properties may be given a readOnly attribute (same as in JSONSchema-draft7)
  • array types must have an items restriction
  • format supports more values for strings and numbers

Other minor differences:

  • default values must validate their schema
  • types array must have an items specification
  • extensions may be specified as special x-... annotations. Go-swagger defines some custom tags to customize generated code.
  • other available attributes: example, xml and externalDocs

NOTE: example and externalDocs do not currently influence models generation.

Go-swagger vs Swagger #

go-swagger models implements almost all Swagger 2.0 schema features.

We also wanted to support as much JSONSchema features as possible: the model generator may be used independently to generate data structures using the Swagger specification as a serialization description language.

There are some small differences or implementation details to be aware of.

FeatureJSON-schema-draft4Swagger 2.0go-swaggerComment
"format"YYYFormats are provided by the extensible go-openapi/strfmt package. See also here
"additionalProperties": {schema}YYYRendered as map[string]T
"additionalProperties": booleanYYpartialRendered as map[string]interface{}
"additionalItems": {schema}YNYRendered with tuple models
"additionalItems": booleanYNpartialSee extensible types
empty object: { "type": "object"}YYYRendered as interface{} (anything) rather than map[string]inferface{} (any JSON object, e.g. not arrays)
"pattern"YYpartialSpeed for strictness trade-off: support go regexp, which slighty differ from JSONSchema ECMA regexp (e.g does not support backtracking)
large number, arbitrary precisionYNN
"readOnly"NYY
"type": [ "object", ... ]YNNJSONSchema multiple types are not supported: use Swagger polymorphism instead
implicit type from values in enumY?NAs of v0.15, when the type is empty, the object is rendered as interface{} and the enum constraint is ignored
tuple `type: “array” items:[…]YYpartialAs of v0.15, incomplete tuples and tuples with array validation are not properly validated

JSONSchema defaults to "additionalProperties": true, go-swagger defaults to ignoring extra properties. Same for additionalItems.

When"additionalProperties": false (resp. "additionalItems": false), uwanted properties (resp. items) do not invalidate data but they are not kept in the model.

This is the default if additionalProperties (resp. additionalItems) is not provided. It is an optimization as it makes the code simpler (and faster) for most use cases. Explicitly specifying true will produce models that retain those additional properties (resp. items).

Known limitations with go-swagger models #

Recap as of release >0.26:

  • re JSON-schema-draft4

    • "additionalProperties": false, "additionalItems": false do not invalidate data with extra properties. We trade strictness for speed and truncate unwanted properties or items without further validation.
    • the generation flag --strict-additional-properties invalidates data with extra properties when "additionalProperties": false
    • when enum values cannot be marshalled into their schema, a runtime panic occurs - the go-openapi/validate package does not yet detect this situation
    • patternProperties and dependenciesare not supported
    • use of additionalItems requires the --skip-validation flag (go-openapi/validate is strict regarding Swagger specification)
    • JSONSchema defaults to the "additionalProperties": true, go-swagger defaults to ignoring extra properties. Same for additionalItems.
    • array validations (minItems, etc.) are not yet supported for tuples, as of v0.15
    • objects with no properties and no additional properties schema have no validation at all (e.g. passing an array is not invalid) (rendered as interface{})
    • null JSON type: the null type is not supported by Swagger - use of the x-nullable extension makes null values valid (notice that combining the use of required and x-nullable is not fully JSONSchema compliant - see below)

Custom extensions #

Model generation may be altered with the following extensions:

  • x-go-name: "string": give explicit type name to the generated model
  • x-go-custom-tag: "string": add serialization tags to an object property (see Customizing struct tags)
  • x-nullable: true|false (or equivalently x-is-nullable:true|false): accepts null values (i.e. rendered as a pointer)
  • x-go-type: "string": explicitly reuse an already available go type
  • x-class: "string": give explicit polymorphic class name in discriminator
  • x-order: number: indicates explicit generation ordering for schemas (e.g. models, properties, allOf, …)
  • x-omitempty: true|false: force the omitempty modifier in struct json and xml tags
  • x-go-json-string: true:false: force the string modifier in struct json tags

Primitive types #

Swagger types are rendered as follows by go-swagger:

Swagger typego type
string (no format)string
booleanbool
numberfloat64
number format doublefloat64
number format floatfloat32
integerint64
integer format int64int64
integer format int32int32
integer format uint64uint64
integer format uint32uint32
fileio.ReadCloser(server) or io.Writer (client)
string format binaryio.ReadCloseror io.Writer
string with other formatscorresponding type exported by go-openapi/strfmt

The file type is exposed as a io.ReadCloser (or io.Writer) interface. The actual implementation in a runtime server or client is provided by the go-openapi/runtime/File type.

Formatted types #

The go-openapi/strfmt packages provides a number of predefined “formats” for JSON string types. The full list of formats supported by this package is here

Nullability #

Here are the rules that turn something into a pointer.

  • structs
  • x-nullable, x-isnullable: explicit override to accept null values (otherwise not accepted by Swagger)
  • required property
  • extending with allOf a schema with another schema with just x-nullable (or other extensions, but no new properties) turns the schema into a pointer

Primitive types (number, bool and string) are turned into pointers whenever:

  • we need to validate valid zero values vs unset (i.e. the zero value is explicitly checked against validation)

Examples:

definitions:
  myInteger:
    type: integer
    minimum: 0
  myString:
    type: string
    minLength: 0

Yields:

type MyInteger *int64
...
type MyString *string

Notice that the following equivalent does not produce a pointer:

definitions:
  myInteger:
    type: integer
    format: uint64

NOTE: read-only properties are not rendered as pointers.

API developers may use the conversion utilities provided by the go-openapi/swag and go-openapi/strfmt/conv packages to manipulate pointers more easily.

Known limitations: pointers are used to distinguish in golang a zero value from no value set.

This design comes with some shortcomings:

  • it is built around the validation use case. In the general case it is not possible to know if a value has been set to a zero value when the type is not a pointer. In cases where this is important, use the x-nullable extension
  • using null as a proxy for unset, makes uneasy the explicit use of the JSON null type Swagger APIs are not supposed to carry null values. go-swagger generated APIs can, using the x-nullable extension, and it is then not possible to distinguish a field explicitly set to null from an unset field

An alternate design has been experimented but not released. For those interested in pushing forward this project again, see this pull request

Common use cases #

You don’t always have to resort to pointers to figure out whether a value is empty.

  • The idiomatic way to check for a null/empty string is: minLength: 1

Validation #

All produced models implement the Validatable interface.

Exceptions:

  • file types do not support validation (however generated operations may check the maxLength of a file)
  • empty schemas (any type, rendered as interface{}) do not support validation

Therefore, type aliases constructed on either a swagger file or an empty schema does not implement this interface.

Validation errors:

  • Returned errors are supported by the go-openapi/errors/Error type, which supports errors codes and composite errors.

Validation stops assessing errors down to the property level and does not continue digging all nested strutures as soon as an error is found.

Type aliasing #

A definition may create an aliased type like this:

definitions:
  myDate:
    type: string
    format: date

Rendered as:

type MyDate strfmt.Date

Notice that setting x-nullable: true in such an alias will not render the type itself into a pointer, but rather, all containers of his type will use it as a pointer.

Example:

definitions:
  myDate:
    type: string
    format: date
    x-nullable: true
  anArrayOfDates:
    type: array
    items:
      $ref: '#/definitions/myDate'

Yields:

type MyDate strfmt.Date
...
type AnArrayOfDates []*MyDate

Realiasing

Given the above definitions, we add:

  ...
  herDate:
    $ref: #/definitions/myDate
  hisDate:
    $ref: #/definitions/herDate

Rendered as (requires go1.9+):

type HerDate = MyDate
type HisDate = HerDate

Extensible types #

Objects and additional properties #

Additional properties in a JSON object are represented in a go struct by map[string]T.

Examples:

definitions:
  extensibleObject:
    properties:
      prop1:
        type:  integer
    additionalProperties:
      type: string
      format: date

Is rendered as:

type ExtensibleObject struct {
    Prop1 int64
    ExtensibleObjectProperties map[string]strfmt.Date
}

If there is no restriction on the additional properties:

definitions:
  extensibleObject:
    type: object
    properties:
      prop1:
        type: integer
    additionalProperties: true

We get:

type ExtensibleObject struct {
    Prop1 int64
    ExtensibleObjectProperties map[string]interface{}
}
Tuples and additional items #

A tuple is rendered as a structure with a property for each element of the tuple.

Example:

definitions:
  tuple:
    type: array
    items:
    - type: integer
    - type: string
    - type: string
      format: uuid

Gives:

type Tuple struct {
    P0 *int64
    P1 *string
    P2 *strfmt.UUID
}

If we specify additional items as in:

definitions:
  extensibleTuple:
    type: array
    items:
    - type: integer
    - type: string
    - type: string
      format: uuid
    additionalItems:
    - type: number

Gives:

type ExtensibleTuple struct {
    P0 *int64
    P1 *string
    P2 *strfmt.UUID
    ExtensibleTupleItems []float64
}

NOTE: currently the P0, P1, … names are not customizable.

Polymorphic types #

Polymorphic types are swagger’s flavor for inheritance (aka hierarchized composition…). The use of the discriminator keyword gives a special meaning to allOf compositions.

Base types #

Whenever the special attribute discriminator is used, this means this object definition is a base type, to be used to extend other types, or subtypes.

The discriminator property indicates which subtype is used whenever an instance of the base type is found. The discriminator’s possible values are the names of the subtypes (no aliasing is supported in Swagger 2.0).

NOTE: the discriminator is a required property with "type": "string". No validation attached to this property, save required, will be honored, as a discriminator property is implicitly an enum with all the subtype names found in the spec.

The base type must be a JSON-schema object (it has at least one property, the discriminator). It may define other properties than the discriminator. Like name in this example.

Example:

  Pet:
    type: object
    discriminator: petType
    properties:
      name:
        type: string
      petType:
        type: string
    required:
    - name
    - petType

A base type is rendered as an interface, with getter/setter funcs on all attributes.

// Pet pet
// swagger:discriminator Pet petType
type Pet interface {
	runtime.Validatable

	// name
	// Required: true
	Name() *string
	SetName(*string)

	// pet type
	// Required: true
	PetType() string
	SetPetType(string)
}

NOTE: an unexported reference concrete type is also generated, but not currently used by models.

Subtypes #

A subtype extends a base type. It is defined by composing the base type with an allOf construct. All subtypes implement the interface of their base type. So in this examples, all instances of Dog may pretend to be a Pet.

Example:

  Dog:
    type: object
    description: A representation of a dog
    allOf:
    - $ref: '#/definitions/Pet'
    - properties:
        packSize:
          type: integer
          format: int32
          description: the size of the pack the dog is from
          default: 0
          minimum: 0
      required:
      - packSize

Yields:

// Dog A representation of a dog
// swagger:model Dog
type Dog struct {
	nameField *string

	// the size of the pack the dog is from
	// Required: true
	// Minimum: 0
	PackSize *int32 `json:"packSize"`
}

// Name gets the name of this subtype
func (m *Dog) Name() *string {
	return m.nameField
}

// SetName sets the name of this subtype
func (m *Dog) SetName(val *string) {
	m.nameField = val
}

// PetType gets the pet type of this subtype
func (m *Dog) PetType() string {
	return "Dog"
}

// SetPetType sets the pet type of this subtype
func (m *Dog) SetPetType(val string) {

}

Notice the unexported fields which correspond to the description of the base type. The properties of the base type are available with getter/setter functions.

NOTE: if you expand your spec, the allOf semantics are lost. Do not expand specs with polymorphic types for code generation.

You may define several such derived types.

Example:

  cat:
    type: object
    description: A representation of a cat
    allOf:
    - $ref: '#/definitions/Pet'
    - properties:
        huntingSkill:
          type: string
          description: The measured skill for hunting
          default: lazy
          enum:
          - clueless
          - lazy
          - adventurous
          - aggressive
      required:
      - huntingSkill
// Cat A representation of a cat
// swagger:model cat
type Cat struct {
	nameField *string

	// The measured skill for hunting
	// Required: true
	// Enum: [clueless lazy adventurous aggressive]
	HuntingSkill *string `json:"huntingSkill"`
}

// Name gets the name of this subtype
func (m *Cat) Name() *string {
	return m.nameField
}

// SetName sets the name of this subtype
func (m *Cat) SetName(val *string) {
	m.nameField = val
}

// PetType gets the pet type of this subtype
func (m *Cat) PetType() string {
	return "cat"
}

// SetPetType sets the pet type of this subtype
func (m *Cat) SetPetType(val string) {

}

Notice that the value of the discriminator field is case sensitive, e.g. "Dog" and "cat" above.

Type composition #

Base types and subtypes may be used in other constructs. While subtypes are mostly handled like ordinary objects, there are special provisions taken to generate new types composing base types.

Example:

  Kennel:
    type: object
    required:
      - pets
    properties:
      id:
        type: integer
        format: int64
      pets:          # <-- this may contain Cats and Dogs
        type: array
        items:
          $ref: "#/definitions/Pet"

Yields:

// Kennel kennel
// swagger:model Kennel
type Kennel struct {

	// id
	ID int64 `json:"id,omitempty"`

	petsField []Pet
}

// Pets gets the pets of this base type
func (m *Kennel) Pets() []Pet {
	return m.petsField
}

// SetPets sets the pets of this base type
func (m *Kennel) SetPets(val []Pet) {
	m.petsField = val
}

NOTE: this representation with unexported fields for references to base types might be subject to change in the future, as it is not consistent in all cases. If you are intested to participate this design work, feel free to comment and express your views here.

Factories for base types #

Subtypes and composed types have custom [un]marshallers.

Unmarshalling a base type is not carried through the standard MarshalJSON()/UnmarshalJSON() pair, but with factories created for each base type.

Example:

// UnmarshalPet unmarshals polymorphic Pet
func UnmarshalPet(reader io.Reader, consumer runtime.Consumer) (Pet, error)

// UnmarshalPetSlice unmarshals polymorphic slices of Pet
func UnmarshalPetSlice(reader io.Reader, consumer runtime.Consumer) ([]Pet, error)

Note that the marshalling of a base type into JSON is processed naturally, so there is no need for a special function.

Known limitations: As of v0.15, there are still some known limitations:

  • Unmarshalling maps of base types is not supported at the moment (e.g. UnmarshalPetMap() factory)
  • More complex constructs like [][]Pet, []map[string]Pet are not supported yet
  • composing tuples containing base types is not supported yet

Serialization interfaces #

Custom serializers #

Tags are generally sufficient to provide proper JSON marshalling capabilities.

Models define some custom [un]marshallers in the following situations:

  • tuples: array elements are dispatched in the tuple’s struct
  • additionalProperties: additional content is dispatched in the map[string]...
  • subtypes of base types
  • types composed of base types
  • aliases on formatted types when the underlying type is not string (e.g. Date, Datetime)
External types #

External types refer to custom type definitions, short-circuiting the use of generated models.

This is helpful for use-cases when custom marshaling or validation is needed.

Models may also be generated once, customized manually, then reused in spec as external types.

The extension annotation to declare an external type is x-go-type.

A complete example is provided here to illustrate the different capabilities to inject custom types.

Examples:

External types are typically used in top-level model definitions, like so:

definitions:
  myType:
    type: object
    x-go-type:
      type: MyExternalType                          # <- abide by go conventions! The type must be exported
      import:
        package: github.com/example/models/custom     # <- use fully qualified package names

Such definitions do not produce any generated model.

References in the generated code to this type will produce code like this:

custom.MyExternalType

If no package is provided, it defaults to the models package indicated for codegen:

definitions:
  generatedType:
    type: array
    items:
      $ref: '#/definitions/myType'

  myType:
    type: object
    x-go-type:
      type: MyExternalType
swagger generate models --model-package custom --target ./codegen

ls ./codegen/custom   # <- myType is NOT GENERATED
cat ./codegen/custom/generated_type.go
package custom

type GeneratedType []MyType

External types may also be injected at lower schema levels:

definitions:
  MyType:
    type: array
    items:
      type: string
      x-go-type:
        type: MyExternalString
        import:
          package: github.com/example/models/custom

or:

  MyObject:
    type: object
    properties:
      p1:
        x-go-type:
          type: RawMessage
          import:
            package: encoding/json
          hints:
            kind: interface

This also works for inlined types defined at the operation level:

  parameters:
  - in: body
    name: corpus
    schema:
      type: object
      x-go-type:
        type: MyExternalStruct
        import:
          package: github.com/example/models/custom

NOTE: when defining inline arrays or maps, you should know that the external type is not considered nullable by default.

Therefore, unless you explicitly hint the generator to consider it nullable, you’ll get constructs such as []external.MyType or map[string]external.MyType instead of []*external.MyType and map[string]*external.MyType respectively. You can use the nullable hint or the x-nullable extension to control this behavior.

Known limitations #
  • External types only apply to schema objects. Simple swagger types used in operations for query or path parameters or for response headers cannot be externalized at this moment.

  • Inlined external types cannot be declared inside polymorphic types (discriminated types).

  • Inlined external types cannot be declared as embedded. Only top-level definitions are supported.

External package aliasing #

The following example replaces all references to myModel by github.com/example/models/MyCustomModel.

Example:

definitions:
  myModel:
    type: object
    x-go-type:
      type: MyCustomModel
      import:
        package: github.com/example/models

Note that the external model must implement the github.com/go-openapi/runtime.Validatable interface: it must know how to validate a schema. No model is generated for this definition.

External packages may be imported with an alias, like so:

  parameters:
    in: body
    schema:
      type: object
      x-go-type:
        type: MyExternalStruct
        import:
          package: github.com/example/models/custom
          alias: fred

Imports will look like so:

import (
  fred "github.com/example/models/custom"
)
...

Some deconfliction with other known import is applied automatically. Automatic deconfliction is not perfect, though.

For example:

  MyObject:
    type: object
    properties:
      p1:
        x-go-type:
          type: RawMessage
          import:
            package: encoding/json
          hints:
            kind: interface
import (
  jsonext "encoding/json"
)
Embedding external types #

Sometimes, it is impractical to impose the constraint that the external type has a validation method. You can then use the “embedded” option to create an embedded type based on the external model, and wraps the Validate method.

Example:

definitions:
  Time:
    type: string
    format: date-time         # <- documentary only (external types takes over). This has no impact on generation.
    x-go-type:
      type: Time
      import:
        package: time
      embedded: true

This example generates a wrapper type in the package model with a Validate method like this:

import (
	timeext "time"

	"github.com/go-openapi/runtime"
	"github.com/go-openapi/strfmt"
	"github.com/go-openapi/swag"
)

// Time time
//
// swagger:model Time
type Time struct {
	timeext.Time
}

func (m Time) Validate(formats strfmt.Registry) error {
	var f interface{} = m.Time
	if v, ok := f.(runtime.Validatable); ok {
		return v.Validate(formats)
	}
	return nil
}

The generated Validate method uses any existing Validate method or just returns nil (i.e. data is valid).

NOTE: at the moment, we do not support the format specification over the embedded type. Format will be documentary only in that case.

Other examples:

  Raw:
    x-go-type:
      type: RawMessage
      import:
        package: encoding/json
      hints:
        kind: primitive
      embedded: true
import (
 ...
 jsonext "encoding/json"
 ...
)

type Raw struct {
	jsonext.RawMessage
}

func (m Raw) Validate(formats strfmt.Registry) error {
	var f interface{} = m.RawMessage
	if v, ok := f.(runtime.Validatable); ok {
		return v.Validate(formats)
	}
	return nil
}

You can embed types as pointers just the same.

Example:

definitions:
  Time:
    type: string
    x-go-type:
      type: Time
      import:
        package: time
      hints:
        nullable: true  # <- nullable here refers to the nullability of the embedded external type
      embedded: true
type Time struct {
	*time.Time
}

Using external types is powerful, but normally you still have to describe your type in the specification. That is expected, since this is how you document your API.

If you don’t (that is the type referred to doesn’t correspond to the type in the spec), then the generator may fail to produce correct code, because it simply has no way to infer what kind of object is being referred to.

To solve this kind of problem, you may hint the generator to produce a correct usage of the external types, even though the specification doesn’t reflect the correct nature of the object.

Example:

definitions:
  Error:
    type: object

  Hotspot:
    x-go-type:
      type: Hotspot
      import:
        package: github.com/go-swagger/go-swagger/fixtures/enhancements/2224/external
      hints:
        kind: object
    x-nullable: true

In this example, the Hotspot schema is empty in the specification. The generator therefore can only guess that this is some interface{} type. Now thanks to the hint kind: object, we instruct the generator to expect an object so as to correctly reference this object.

Validation of external types #

By default, the generator assumes that external types can be validated and will generate code that calls the “Validate” method of the type.

This can be disabled by providing an explicit hint:

  MyObject:
    type: object
    properties:
      p1:
        x-go-type:
          type: RawMessage
          import:
            package: encoding/json
          hints:
            noValidation: true

External types with an hint type “interface” or “stream” do not call validations.

Embedded types use type assertion to dynamically determine if the external type implements the runtime.Validatable interface.

External type hints #

The generator does not attempt to introspect external types. They may even not exist at generation time.

Therefore, the generator has no idea of whether it is safe to generate pointers to the external type.

By default, external types are considered non nullable. This can be altered with the nullable hint or by hinting a type that is considered nullable (such as “object”).

Supported hints:

        x-go-type:
          type: {external type name (exported symbol, without package qualifier)}
          import:
            package: {fully qualified package name - defaults to the target models defined by the --model-package flag}
          hints:
            kind: {map|object|array|interface|primitive|tuple|stream}
            noValidation: true|false  # <- skips validation: defaults to true for embedded types, defaults to false for non-embedded, always false for kinds interface and stream
            nullable: true|false      # <- default to true for kinds object,primitive and tuple
          embedded: true|false        # <- defaults to false, generates a struct that wraps the external type
Caveats with imports #

At this moment, external packages and aliases are deconflicted against other known imports and variables.

Example:

  MyObject:
    type: object
    properties:
      p1:
        x-go-type:
          type: RawMessage
          import:
            package: encoding/json
          hints:
            kind: interface

will generate an import deconflicted against the standard lib import:

import(
    ...
    jsonext "encoding/json"
    ...
    )
External package aliasing #

External packages may be imported with an alias, like so:

  parameters:
    in: body
    schema:
      type: object
      x-go-type:
        type: MyExternalStruct
        import:
          package: github.com/example/models/custom
          alias: fred

Imports will look like so:

import (
  fred "github.com/example/models/custom"
)
...

Some deconfliction with other known imports is applied automatically. Automatic deconfliction is not perfect, though.

For example:

  MyObject:
    type: object
    properties:
      p1:
        x-go-type:
          type: RawMessage
          import:
            package: encoding/json
          hints:
            kind: interface
import (
  jsonext "encoding/json"
)

Package aliases may still conflict with packages produces by operation tags or other external imports.

In such cases, modify the type alias under x-go-type to resolve the conflict manually.

Customizing struct tags #

When a model struct is generated, tags for json are generated to keep the original name:

type ObjectWithTag struct {
    StandardTag string `json:"standardTag,omitempty"`
}

Extra tags #

Extra tags may be defined with the CLI generation option --sruct-tags.

Extra tags essentially repeat the name of the field can be added from the command line option.

NOTE: at this moment, all tag modifiers (omitempty, string) are repeated like for the json tag.

swagger generate model ... --struct-tags yaml,db
type ObjectWithTag struct {
    StandardTag string `json:"standardTag,omitempty" yaml:"standardTag,omitempty" db:"standardTag,omitempty"`
}

Custom tags #

A custom may be added to a field using the x-go-custom-tag extension. Like so:

Omit empty values #

By default, a struct field is omitted when it holds the zero value (tag modifier: omitempty).

Required fields are never omitted.

type ObjectWithTag struct {
    RequiredField *string `json:"requiredField"`
}

This property can be altered using the x-omitempty extension. Like so:

objectWithTag:
  type: object
  properties:
    field:
      type: string
      x-omitempty: false
type ObjectWithTag struct {
    Field string `json:"field"`
}

The extension does not force a required field to get the “omitempty” modifier.

Numerical values as string #

For some specific requirements, the standard json library may consider numbers as strings. This is done by adding the modifier json:"...,string" to the tag.

With go-swagger you can specify this modifier by adding the x-go-json-string: true extension to your type.

type ObjectWithTag struct {
    NumericField int `json:"field,omitempty,string"`
}

XML tags #

The XML name and attribute Swagger properties are used to generate extra tags.

definitions:
 objectWithXML:
   type: object
   properties:
     field:
       type: string
       xml:
         name: xmlObject
         attribute: true
type ObjectWithXML struct {
    Field string `json:"field,omitempty" xml:"xmlObject,attr,omitempty"`
}

The example tag #

If you add example to the list of generated tags from the CLI (swagger generate ... --struct-tags example), a special example tag is created with the example value taken from the specification.

definitions:
  objectWithExample:
   properties:
     field:
       type: string
       example: "sample"
type ObjectWithExample struct {
    Field string `json:"field,omitempty" example:"\"sample\""`
}

The description tag #

If you add description to the list of generated tags from the CLI (swagger generate ... --struct-tags description), a special description tag is created with the description value taken from the specification.

definitions:
  objectWithDescription:
   properties:
     field:
       type: string
       description: "some description"
type ObjectWithDescription struct {
    Field string `json:"field,omitempty" description:"\"some description\""`
}

Copyright 2015-2024 the go-swagger maintainers. This documentation is under an Apache 2.0 license.

Site built with HUGO using hugo-book