github.com/go-swagger/go-swagger@v0.31.0/docs/reference/models/schemas.md (about) 1 --- 2 title: Swagger schema generation rules 3 date: 2023-01-01T01:01:01-08:00 4 draft: true 5 --- 6 # Schema generation rules 7 8 Lots of the work carried out by go-swagger is to generate models, which can have all kinds of rules like polymorphism and 9 validations. Go-swagger models are the go data structures used for serialization and validation. 10 11 Of course none of this is possible without a set of rules and trade-offs. 12 13 ### About schemas 14 15 A schema is a data structure specified in a Swagger document. Loosely speaking, a swagger schema corresponds to 16 a JSONSchema-draft4 schema (see [differences](#swagger-vs-jsonschema) below). 17 For each schema, `go-swagger` will generate one or more model types in go. 18 19 > **NOTE**: Swagger makes a distinction between schemas and "simple schemas". 20 > We use simple schemas to describe parameters and headers for operations. 21 > Simple schemas are supported by `go-swagger` (including validation), but are not rendered as standalone models. 22 > Rather, simple schema structures and validation methods are generated with the operation they are attached to. 23 > 24 > Models are dedicated to the description of Swagger schemas, which are described as a parameter body, a response or a 25 > spec definition. 26 27 #### Interfaces 28 29 The generated models implements: 30 31 - a serialization interface 32 - `MarshalJSON()`, `UnmarshalJSON()`: 33 - standard structures use JSON decoder tags (serialization tags are customizable) 34 - composed and extensible structures use custom marshalers (`allOf`, `additionalProperties`, tuples and polymorphic types) 35 - `MarshalBinary()`, `UnmarshalBinary()` interfaces (`encoding/BinaryMarshaler`, `encoding/BinaryUnmarshaler`), 36 which may use the fast [`mailru/easyjson`][easy-json] package 37 - a validation interface ([`go-openapi/runtime/Validatable`][Validatable]), with a `Validate(strfmt.Registry) error` method 38 39 Validation methods are wired at generation time, and rely mostly on native types: this makes validation faster than a 40 dynamic general purpose JSON schema validator. 41 42 Example of a generated structure: 43 ```golang 44 // Principal principal 45 // swagger:model principal 46 type Principal struct { 47 48 // name 49 Name string `json:"name,omitempty"` 50 51 // roles 52 Roles []string `json:"roles"` 53 } 54 ``` 55 56 > **NOTE**: if you are looking for a **dynamic**, fully JSONSchema compliant, general purpose validator, 57 > the [`go-openapi/validate`][validate] package 58 > is what you want. It is fully tested against the JSONSchema-Test-Suite (supports JSON-schema-draft4). 59 > See a working example [here][validate-json]. 60 61 #### Mapping patterns 62 63 The general idea is that you should rarely see `interface{}` in the generated code: 64 you get a complete representation of a swagger document in somewhat idiomatic go. 65 66 There is set of mapping patterns that are applied to transform a spec into go types: 67 68 * definition of primitive => type alias/name 69 * definition of array => type alias/name 70 * definition of map => type alias/name 71 * definition of object with properties => struct 72 * definition of $ref => type alias/name 73 * object with only additional properties => map[string]T 74 * object with additional properties and properties => custom serializer 75 * schema with schema array in items => tuple (struct with properties, custom serializer) 76 * schema with all of => struct 77 * all of schema with ref => embedded value 78 * all of schema with properties => properties are included in struct 79 * adding an all of schema with just `x-isnullable`: true or `x-nullable`: true turns 80 the schema into a pointer when there are only other extension properties provided 81 * additional items in tuples => 82 JSONSchema and by extension swagger allow for items that have a fixed size array 83 with schema's describing the items at each index. This can be combined with additional items 84 to form some kind of tuple with varargs. 85 To map this to go it creates a struct that has fixed names and a custom json serializer. 86 87 #### Minimal use of go's reflection 88 89 Generated models uses _no reflection_ except for `enum` and `required` validations. This makes validation faster. 90 91 #### Doc strings 92 93 All documentation items provided by the spec are integrated as godoc-friendly comments in the 94 generated source. You may look at how a trivial example is rendered [here][go-doc-model]. 95 96 The code that is generated also gets the same doc comments that are used by the scanner 97 to generate a spec from go code (e.g. comments like `// swagger:xxx`). 98 So that after generation you should be able to reverse-generate a spec from the code that was generated by your spec. 99 It should be equivalent to the original spec but might miss some default values and examples. 100 101 #### Types reusability 102 103 Models can be generated independently from other components of your API. 104 Internal model structures may thus be safely regenerated if the contract at your endpoints does not change. 105 106 ##### Reusing previous generations 107 108 Previously generated models can be reused when constructing a new API server or client (e.g. using `swagger generate server --model=[my existing package]`). 109 110 The generator makes every effort to keep the go code readable, idiomatic and commented: models may thus be manually customized or extended. 111 Such customized types may be later on reused in other specs, using the `x-go-type` extension. 112 113 114 ### Swagger vs JSONSchema 115 116 A Swagger 2.0 schema corresponds by and large to a JSON-schema-draft4. However, there are some substantial differences (see [swagger]): 117 118 In JSONSchema, but _not_ in Swagger 2.0: 119 120 - `anyOf`, `oneOf` and `not` constructs are not supported (this is for OpenAPI 3) 121 - the `null` type is not supported (the `nullable` keyword is defined in OpenAPI 3) 122 - `additionalItems` are not supported (go-swagger does support it) 123 - `patternProperties` are not supported 124 - `dependencies` are not supported 125 - multiple types, defined as `type: [ ... ]` are not supported 126 127 Conversely, what we have in Swagger 2.0, but _not_ in JSON-schema: 128 129 - the `discriminator` attribute controls polymorphism ([see below](#polymorphic-types)) 130 - properties may be given a [`readOnly` attribute][read-only] (same as in JSONSchema-draft7) 131 - `array` types **must** have an `items` restriction 132 - `format` supports more values for strings and numbers 133 134 Other minor differences: 135 136 - `default` values **must** validate their schema 137 - types `array` **must** have an `items` specification 138 - extensions may be specified as special `x-...` annotations. Go-swagger defines [some custom tags](#custom-extensions) to customize generated code. 139 - other available attributes: `example`, `xml` and `externalDocs` 140 141 > **NOTE**: `example` and `externalDocs` do not currently influence models generation. 142 143 ### Go-swagger vs Swagger 144 145 go-swagger models implements _almost_ all Swagger 2.0 schema features. 146 147 We also wanted to support as much JSONSchema features as possible: the model generator may be used independently 148 to generate data structures using the Swagger specification as a serialization description language. 149 150 There are some small differences or implementation details to be aware of. 151 152 | Feature | JSON-schema-draft4 | Swagger 2.0 | go-swagger | Comment | 153 |--- |--- |--- |--- |--- | 154 | `"format"` | Y| Y | Y | Formats are provided by the extensible [`go-openapi/strfmt` package][strfmt]. See also [here](#formatted-types)| 155 | `"additionalProperties": {schema}` | Y| Y | Y | Rendered as `map[string]T` | 156 | `"additionalProperties": boolean` | Y| Y | partial| Rendered as `map[string]interface{}` | 157 | `"additionalItems": {schema}` | Y| **N**| Y | Rendered with [tuple models](#tuples-and-additional-items)| 158 | `"additionalItems": boolean` | Y| **N**| partial| See [extensible types](#extensible-types) | 159 | empty object: `{ "type": "object"}` | Y| Y | Y | Rendered as `interface{}` (anything) rather than `map[string]inferface{}` (any JSON object, e.g. not arrays)| 160 | `"pattern"` | Y| Y | partial| Speed for strictness trade-off: support go regexp, which slighty differ from JSONSchema ECMA regexp (e.g does not support backtracking)| 161 | large number, arbitrary precision | Y| **N**| N | | 162 | `"readOnly"` | N| Y | Y | | 163 | `"type": [ "object", ... ]` | Y| N | N | JSONSchema multiple types are not supported: use Swagger polymorphism instead| 164 | implicit type from values in `enum` | Y| ? | N | As of v0.15, when the type is empty, the object is rendered as `interface{}` and the `enum` constraint is ignored| 165 | tuple `type: "array" items:[...] | Y| Y | partial| As of v0.15, incomplete tuples and tuples with array validation are not properly validated| 166 167 168 JSONSchema defaults to `"additionalProperties": true`, `go-swagger` defaults to ignoring extra properties. Same for `additionalItems`. 169 170 When`"additionalProperties": false` (resp. `"additionalItems": false`), uwanted properties (resp. items) do not invalidate data 171 but they are not kept in the model. 172 173 This is the default if `additionalProperties` (resp. `additionalItems`) is not provided. 174 It is an optimization as it makes the code simpler (and faster) for most use cases. 175 Explicitly specifying `true` will produce models that retain those additional properties (resp. items). 176 177 ### Known limitations with go-swagger models 178 179 Recap as of release `>0.26`: 180 181 - re [JSON-schema-draft4][json-schema] 182 183 - `"additionalProperties": false`, `"additionalItems": false` do not invalidate data with extra properties. We trade strictness for speed and 184 truncate unwanted properties or items without further validation. 185 - the generation flag `--strict-additional-properties` invalidates data with extra properties when `"additionalProperties": false` 186 - when `enum` values cannot be marshalled into their schema, a runtime panic occurs - the `go-openapi/validate` package does not yet detect this situation 187 - `patternProperties` and `dependencies`are not supported 188 - use of `additionalItems` requires the `--skip-validation` flag (`go-openapi/validate` is strict regarding Swagger specification) 189 - JSONSchema defaults to the `"additionalProperties": true`, `go-swagger` defaults to ignoring extra properties. Same for `additionalItems`. 190 - array validations (`minItems`, etc.) are not yet supported for tuples, as of v0.15 191 - 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{}`) 192 - `null` JSON type: the `null` type is not supported by Swagger - use of the `x-nullable` extension makes `null` values valid 193 (notice that combining the use of `required` and `x-nullable` is not fully JSONSchema compliant - see [below](#nullability)) 194 195 ### Custom extensions 196 197 Model generation may be altered with the following extensions: 198 199 - `x-go-name: "string"`: give explicit type name to the generated model 200 - `x-go-custom-tag: "string"`: add serialization tags to an object property (see [Customizing struct tags](#customizing-struct-tags)) 201 - `x-nullable: true|false` (or equivalently `x-is-nullable:true|false`): accepts null values (i.e. rendered as a pointer) 202 - `x-go-type: "string"`: explicitly reuse an already available go type 203 - `x-class: "string"`: give explicit polymorphic class name in discriminator 204 - `x-order: number`: indicates explicit generation ordering for schemas (e.g. models, properties, allOf, ...) 205 - `x-omitempty: true|false`: force the omitempty modifier in struct json and xml tags 206 - `x-go-json-string: true:false`: force the string modifier in struct json tags 207 208 ### Primitive types 209 210 Swagger types are rendered as follows by `go-swagger`: 211 212 | Swagger type | go type | 213 |-----------------------------|----------| 214 | `string` (no format) | `string` | 215 | `boolean` | `bool` | 216 | `number` | `float64`| 217 | `number format double` | `float64`| 218 | `number format float` | `float32`| 219 | `integer` | `int64` | 220 | `integer format int64` | `int64` | 221 | `integer format int32` | `int32` | 222 | `integer format uint64` | `uint64` | 223 | `integer format uint32` | `uint32` | 224 | `file` | `io.ReadCloser`(server) or `io.Writer` (client)| 225 | `string format binary` | `io.ReadCloser`or `io.Writer`| 226 | `string` with other formats | corresponding type exported by `go-openapi/strfmt` | 227 228 The `file` type is exposed as a `io.ReadCloser` (or `io.Writer`) interface. The actual implementation in a 229 runtime server or client is provided by the [`go-openapi/runtime/File` type][File]. 230 231 ### Formatted types 232 233 The `go-openapi/strfmt` packages provides a number of predefined "formats" for JSON string types. 234 The full list of formats supported by this package is [here][all-formats] 235 236 ### Nullability 237 238 Here are the rules that turn something into a pointer. 239 * structs 240 * `x-nullable`, `x-isnullable`: explicit override to accept null values (otherwise not accepted by Swagger) 241 * required property 242 * extending with `allOf` a schema with another schema with just `x-nullable` (or other extensions, 243 but no new properties) turns the schema into a pointer 244 245 Primitive types (number, bool and string) are turned into pointers whenever: 246 * we need to validate valid zero values vs unset (i.e. the zero value is explicitly checked against validation) 247 248 Examples: 249 ```yaml 250 definitions: 251 myInteger: 252 type: integer 253 minimum: 0 254 myString: 255 type: string 256 minLength: 0 257 ``` 258 259 Yields: 260 ```go 261 type MyInteger *int64 262 ... 263 type MyString *string 264 ``` 265 266 Notice that the following equivalent does not produce a pointer: 267 ```yaml 268 definitions: 269 myInteger: 270 type: integer 271 format: uint64 272 ``` 273 274 > NOTE: read-only properties are not rendered as pointers. 275 276 API developers may use the conversion utilities provided by the `go-openapi/swag` and `go-openapi/strfmt/conv` packages 277 to manipulate pointers more easily. 278 279 > **Known limitations**: 280 > pointers are used to distinguish in golang a zero value from no value set. 281 > 282 > This design comes with some shortcomings: 283 > 284 > - it is built around the validation use case. 285 > In the general case it is not possible to know if a value has been set 286 > to a zero value when the type is not a pointer. In cases where this is 287 > important, use the `x-nullable` extension 288 > - using `null` as a proxy for unset, makes uneasy the explicit use of the JSON `null` type 289 > Swagger APIs are not supposed to carry `null` values. 290 > `go-swagger` generated APIs can, using the `x-nullable` extension, and it is then not possible 291 > to distinguish a field explicitly set to `null` from an unset field 292 > 293 > An alternate design has been experimented but not released. For those interested in pushing forward this project again, 294 > see [this pull request][lifting-pointers] 295 296 #### Common use cases 297 298 You don't always have to resort to pointers to figure out whether a value is empty. 299 300 * The idiomatic way to check for a null/empty string is: `minLength: 1` 301 302 ### Validation 303 304 All produced models implement the [Validatable] interface. 305 306 Exceptions: 307 - `file` types do not support validation (however generated operations may check the `maxLength` of a file) 308 - empty schemas (`any` type, rendered as `interface{}`) do not support validation 309 310 Therefore, type aliases constructed on either a swagger `file` or an empty schema does not implement this interface. 311 312 Validation errors: 313 - Returned errors are supported by the `go-openapi/errors/Error` type, which supports errors codes and composite errors. 314 315 Validation stops assessing errors down to the property level and does not continue digging all nested strutures as soon 316 as an error is found. 317 318 ### Type aliasing 319 320 A definition may create an _aliased_ type like this: 321 322 ```yaml 323 definitions: 324 myDate: 325 type: string 326 format: date 327 ``` 328 329 Rendered as: 330 ```go 331 type MyDate strfmt.Date 332 ``` 333 334 Notice that setting `x-nullable: true` in such an alias will not render the type itself into a pointer, but rather, 335 all containers of his type will use it as a pointer. 336 337 Example: 338 ```yaml 339 definitions: 340 myDate: 341 type: string 342 format: date 343 x-nullable: true 344 anArrayOfDates: 345 type: array 346 items: 347 $ref: '#/definitions/myDate' 348 ``` 349 350 Yields: 351 ```go 352 type MyDate strfmt.Date 353 ... 354 type AnArrayOfDates []*MyDate 355 ``` 356 357 Realiasing 358 359 Given the above definitions, we add: 360 361 ```yaml 362 ... 363 herDate: 364 $ref: #/definitions/myDate 365 hisDate: 366 $ref: #/definitions/herDate 367 ``` 368 369 Rendered as (requires go1.9+): 370 ```go 371 type HerDate = MyDate 372 ``` 373 374 ```go 375 type HisDate = HerDate 376 ``` 377 378 ### Extensible types 379 380 ##### Objects and additional properties 381 382 Additional properties in a JSON object are represented in a go struct by `map[string]T`. 383 384 Examples: 385 386 ```yaml 387 definitions: 388 extensibleObject: 389 properties: 390 prop1: 391 type: integer 392 additionalProperties: 393 type: string 394 format: date 395 ``` 396 397 Is rendered as: 398 ```golang 399 type ExtensibleObject struct { 400 Prop1 int64 401 ExtensibleObjectProperties map[string]strfmt.Date 402 } 403 ``` 404 405 If there is no restriction on the additional properties: 406 407 ```yaml 408 definitions: 409 extensibleObject: 410 type: object 411 properties: 412 prop1: 413 type: integer 414 additionalProperties: true 415 ``` 416 417 We get: 418 ```golang 419 type ExtensibleObject struct { 420 Prop1 int64 421 ExtensibleObjectProperties map[string]interface{} 422 } 423 ``` 424 425 ##### Tuples and additional items 426 427 A tuple is rendered as a structure with a property for each element of the tuple. 428 429 Example: 430 ```yaml 431 definitions: 432 tuple: 433 type: array 434 items: 435 - type: integer 436 - type: string 437 - type: string 438 format: uuid 439 ``` 440 441 Gives: 442 ```golang 443 type Tuple struct { 444 P0 *int64 445 P1 *string 446 P2 *strfmt.UUID 447 } 448 ``` 449 450 If we specify additional items as in: 451 ```yaml 452 definitions: 453 extensibleTuple: 454 type: array 455 items: 456 - type: integer 457 - type: string 458 - type: string 459 format: uuid 460 additionalItems: 461 - type: number 462 ``` 463 Gives: 464 ```golang 465 type ExtensibleTuple struct { 466 P0 *int64 467 P1 *string 468 P2 *strfmt.UUID 469 ExtensibleTupleItems []float64 470 } 471 ``` 472 473 > **NOTE**: currently the P0, P1, ... names are not customizable. 474 475 ### Polymorphic types 476 477 Polymorphic types are swagger's flavor for inheritance (aka _hierarchized composition_...). 478 The use of the `discriminator` keyword gives a special meaning to `allOf` compositions. 479 480 #### Base types 481 Whenever the special attribute `discriminator` is used, this means this object definition is 482 a base type, to be used to extend other types, or subtypes. 483 484 The discriminator property indicates which subtype is used whenever an instance of the base type is found. 485 The discriminator's possible values are the names of the subtypes (no aliasing is supported in Swagger 2.0). 486 487 > **NOTE**: the discriminator is a `required` property with `"type": "string"`. 488 > No validation attached to this property, save `required`, will be honored, as 489 > a discriminator property is implicitly an `enum` with all the subtype names found in the spec. 490 491 The base type must be a JSON-schema object (it has at least one property, the discriminator). 492 It may define other properties than the discriminator. Like `name` in this example. 493 494 Example: 495 ```yaml 496 Pet: 497 type: object 498 discriminator: petType 499 properties: 500 name: 501 type: string 502 petType: 503 type: string 504 required: 505 - name 506 - petType 507 ``` 508 509 A base type is rendered as an interface, with getter/setter funcs on all attributes. 510 511 ```go 512 // Pet pet 513 // swagger:discriminator Pet petType 514 type Pet interface { 515 runtime.Validatable 516 517 // name 518 // Required: true 519 Name() *string 520 SetName(*string) 521 522 // pet type 523 // Required: true 524 PetType() string 525 SetPetType(string) 526 } 527 ``` 528 529 > **NOTE**: an unexported reference concrete type is also generated, but not currently used by models. 530 531 #### Subtypes 532 533 A subtype _extends_ a base type. It is defined by composing the base type with an `allOf` construct. 534 All subtypes implement the interface of their base type. So in this examples, all instances of `Dog` may pretend 535 to be a `Pet`. 536 537 Example: 538 ```yaml 539 Dog: 540 type: object 541 description: A representation of a dog 542 allOf: 543 - $ref: '#/definitions/Pet' 544 - properties: 545 packSize: 546 type: integer 547 format: int32 548 description: the size of the pack the dog is from 549 default: 0 550 minimum: 0 551 required: 552 - packSize 553 ``` 554 555 Yields: 556 ```go 557 // Dog A representation of a dog 558 // swagger:model Dog 559 type Dog struct { 560 nameField *string 561 562 // the size of the pack the dog is from 563 // Required: true 564 // Minimum: 0 565 PackSize *int32 `json:"packSize"` 566 } 567 568 // Name gets the name of this subtype 569 func (m *Dog) Name() *string { 570 return m.nameField 571 } 572 573 // SetName sets the name of this subtype 574 func (m *Dog) SetName(val *string) { 575 m.nameField = val 576 } 577 578 // PetType gets the pet type of this subtype 579 func (m *Dog) PetType() string { 580 return "Dog" 581 } 582 583 // SetPetType sets the pet type of this subtype 584 func (m *Dog) SetPetType(val string) { 585 586 } 587 ``` 588 589 Notice the unexported fields which correspond to the description of the base type. 590 The properties of the base type are available with getter/setter functions. 591 592 > **NOTE**: if you expand your spec, the `allOf` semantics are lost. 593 > Do not expand specs with polymorphic types for code generation. 594 595 You may define several such derived types. 596 597 Example: 598 ```yaml 599 cat: 600 type: object 601 description: A representation of a cat 602 allOf: 603 - $ref: '#/definitions/Pet' 604 - properties: 605 huntingSkill: 606 type: string 607 description: The measured skill for hunting 608 default: lazy 609 enum: 610 - clueless 611 - lazy 612 - adventurous 613 - aggressive 614 required: 615 - huntingSkill 616 ``` 617 618 ```go 619 // Cat A representation of a cat 620 // swagger:model cat 621 type Cat struct { 622 nameField *string 623 624 // The measured skill for hunting 625 // Required: true 626 // Enum: [clueless lazy adventurous aggressive] 627 HuntingSkill *string `json:"huntingSkill"` 628 } 629 630 // Name gets the name of this subtype 631 func (m *Cat) Name() *string { 632 return m.nameField 633 } 634 635 // SetName sets the name of this subtype 636 func (m *Cat) SetName(val *string) { 637 m.nameField = val 638 } 639 640 // PetType gets the pet type of this subtype 641 func (m *Cat) PetType() string { 642 return "cat" 643 } 644 645 // SetPetType sets the pet type of this subtype 646 func (m *Cat) SetPetType(val string) { 647 648 } 649 ``` 650 651 Notice that the value of the discriminator field is case sensitive, e.g. `"Dog"` and `"cat"` above. 652 653 #### Type composition 654 655 Base types and subtypes may be used in other constructs. While subtypes are mostly handled like ordinary objects, 656 there are special provisions taken to generate new types composing base types. 657 658 Example: 659 ```yaml 660 Kennel: 661 type: object 662 required: 663 - pets 664 properties: 665 id: 666 type: integer 667 format: int64 668 pets: # <-- this may contain Cats and Dogs 669 type: array 670 items: 671 $ref: "#/definitions/Pet" 672 ``` 673 674 Yields: 675 ```go 676 // Kennel kennel 677 // swagger:model Kennel 678 type Kennel struct { 679 680 // id 681 ID int64 `json:"id,omitempty"` 682 683 petsField []Pet 684 } 685 686 // Pets gets the pets of this base type 687 func (m *Kennel) Pets() []Pet { 688 return m.petsField 689 } 690 691 // SetPets sets the pets of this base type 692 func (m *Kennel) SetPets(val []Pet) { 693 m.petsField = val 694 } 695 ``` 696 697 > **NOTE**: this representation with unexported fields for references to base types might be subject to change in 698 > the future, as it is not consistent in all cases. If you are intested to participate this design work, 699 > feel free to comment and express your views [here](https://github.com/go-swagger/go-swagger/issues/232). 700 701 #### Factories for base types 702 703 Subtypes and composed types have custom [un]marshallers. 704 705 Unmarshalling a base type is not carried through the standard MarshalJSON()/UnmarshalJSON() pair, but with 706 factories created for each base type. 707 708 Example: 709 ```go 710 // UnmarshalPet unmarshals polymorphic Pet 711 func UnmarshalPet(reader io.Reader, consumer runtime.Consumer) (Pet, error) 712 713 // UnmarshalPetSlice unmarshals polymorphic slices of Pet 714 func UnmarshalPetSlice(reader io.Reader, consumer runtime.Consumer) ([]Pet, error) 715 ``` 716 717 Note that the marshalling of a base type into JSON is processed naturally, so there is no need for a special function. 718 719 > **Known limitations**: 720 > As of v0.15, there are still some known limitations: 721 > 722 > - Unmarshalling maps of base types is not supported at the moment (e.g. `UnmarshalPetMap()` factory) 723 > - More complex constructs like `[][]Pet`, `[]map[string]Pet` are not supported yet 724 > - composing tuples containing base types is not supported yet 725 726 ### Serialization interfaces 727 728 <!-- 729 730 JSON 731 732 XML 733 --> 734 735 ##### Custom serializers 736 737 Tags are generally sufficient to provide proper JSON marshalling capabilities. 738 739 Models define some custom [un]marshallers in the following situations: 740 - tuples: array elements are dispatched in the tuple's struct 741 - additionalProperties: additional content is dispatched in the `map[string]...` 742 - subtypes of base types 743 - types composed of base types 744 - aliases on formatted types when the underlying type is not string (e.g. Date, Datetime) 745 746 <!-- 747 ##### [Un]MarshalBinary interfaces 748 749 TODO 750 --> 751 752 <!-- References --> 753 754 ##### External types 755 756 External types refer to custom type definitions, short-circuiting the use of generated models. 757 758 This is helpful for use-cases when custom marshaling or validation is needed. 759 760 Models may also be generated once, customized manually, then reused in spec as external types. 761 762 The extension annotation to declare an external type is `x-go-type`. 763 764 A complete example is provided [here](https://github.com/go-swagger/go-swagger/tree/master/examples/external-types) 765 to illustrate the different capabilities to inject custom types. 766 767 768 Examples: 769 770 External types are typically used in top-level model definitions, like so: 771 772 ```yaml 773 definitions: 774 myType: 775 type: object 776 x-go-type: 777 type: MyExternalType # <- abide by go conventions! The type must be exported 778 import: 779 package: github.com/example/models/custom # <- use fully qualified package names 780 ``` 781 782 Such definitions do not produce any generated model. 783 784 References in the generated code to this type will produce code like this: 785 786 ```go 787 custom.MyExternalType 788 ``` 789 790 If no package is provided, it defaults to the models package indicated for codegen: 791 ```yaml 792 definitions: 793 generatedType: 794 type: array 795 items: 796 $ref: '#/definitions/myType' 797 798 myType: 799 type: object 800 x-go-type: 801 type: MyExternalType 802 ``` 803 804 ```sh 805 swagger generate models --model-package custom --target ./codegen 806 807 ls ./codegen/custom # <- myType is NOT GENERATED 808 cat ./codegen/custom/generated_type.go 809 ``` 810 811 ```go 812 package custom 813 814 type GeneratedType []MyType 815 ``` 816 817 External types may also be injected at lower schema levels: 818 819 ```yaml 820 definitions: 821 MyType: 822 type: array 823 items: 824 type: string 825 x-go-type: 826 type: MyExternalString 827 import: 828 package: github.com/example/models/custom 829 ``` 830 or: 831 ```yaml 832 MyObject: 833 type: object 834 properties: 835 p1: 836 x-go-type: 837 type: RawMessage 838 import: 839 package: encoding/json 840 hints: 841 kind: interface 842 ``` 843 844 This also works for inlined types defined at the operation level: 845 846 ```yaml 847 parameters: 848 - in: body 849 name: corpus 850 schema: 851 type: object 852 x-go-type: 853 type: MyExternalStruct 854 import: 855 package: github.com/example/models/custom 856 ``` 857 858 > **NOTE**: when defining inline arrays or maps, you should know that the external type is 859 > not considered nullable by default. 860 > 861 > Therefore, unless you explicitly hint the generator to consider it nullable, you'll 862 > get constructs such as `[]external.MyType` or `map[string]external.MyType` instead of `[]*external.MyType` 863 > and `map[string]*external.MyType` respectively. You can use the `nullable` hint or the `x-nullable` extension 864 > to control this behavior. 865 866 ###### Known limitations 867 868 * External types only apply to schema objects. Simple swagger types 869 used in operations for query or path parameters or for response headers 870 cannot be externalized at this moment. 871 872 * Inlined external types cannot be declared inside polymorphic types (discriminated types). 873 874 * Inlined external types cannot be declared as embedded. Only top-level definitions are supported. 875 876 877 ###### External package aliasing 878 879 880 The following example replaces all references to `myModel` by `github.com/example/models/MyCustomModel`. 881 882 Example: 883 884 ```yaml 885 definitions: 886 myModel: 887 type: object 888 x-go-type: 889 type: MyCustomModel 890 import: 891 package: github.com/example/models 892 ``` 893 894 Note that the external model must implement the `github.com/go-openapi/runtime.Validatable` interface: it must know how to validate a schema. 895 No model is generated for this definition. 896 897 External packages may be imported with an alias, like so: 898 899 ```yaml 900 parameters: 901 in: body 902 schema: 903 type: object 904 x-go-type: 905 type: MyExternalStruct 906 import: 907 package: github.com/example/models/custom 908 alias: fred 909 ``` 910 911 Imports will look like so: 912 ```go 913 import ( 914 fred "github.com/example/models/custom" 915 ) 916 ... 917 ``` 918 919 Some deconfliction with other known import is applied automatically. Automatic deconfliction is not perfect, though. 920 921 For example: 922 ```yaml 923 MyObject: 924 type: object 925 properties: 926 p1: 927 x-go-type: 928 type: RawMessage 929 import: 930 package: encoding/json 931 hints: 932 kind: interface 933 ``` 934 935 ```go 936 import ( 937 jsonext "encoding/json" 938 ) 939 ``` 940 941 942 ###### Embedding external types 943 944 Sometimes, it is impractical to impose the constraint that the external type has a validation method. 945 You can then use the "embedded" option to create an embedded type based on the external model, and wraps the Validate 946 method. 947 948 Example: 949 ```yaml 950 definitions: 951 Time: 952 type: string 953 format: date-time # <- documentary only (external types takes over). This has no impact on generation. 954 x-go-type: 955 type: Time 956 import: 957 package: time 958 embedded: true 959 ``` 960 961 This example generates a wrapper type in the package model with a `Validate` method like this: 962 963 ```go 964 import ( 965 timeext "time" 966 967 "github.com/go-openapi/runtime" 968 "github.com/go-openapi/strfmt" 969 "github.com/go-openapi/swag" 970 ) 971 972 // Time time 973 // 974 // swagger:model Time 975 type Time struct { 976 timeext.Time 977 } 978 979 func (m Time) Validate(formats strfmt.Registry) error { 980 var f interface{} = m.Time 981 if v, ok := f.(runtime.Validatable); ok { 982 return v.Validate(formats) 983 } 984 return nil 985 } 986 ``` 987 988 The generated `Validate` method uses any existing `Validate` method or just returns `nil` (i.e. data is valid). 989 990 > **NOTE**: at the moment, we do not support the `format` specification over the embedded type. Format will be documentary only in that case. 991 992 Other examples: 993 ```yaml 994 Raw: 995 x-go-type: 996 type: RawMessage 997 import: 998 package: encoding/json 999 hints: 1000 kind: primitive 1001 embedded: true 1002 ``` 1003 ```go 1004 import ( 1005 ... 1006 jsonext "encoding/json" 1007 ... 1008 ) 1009 1010 type Raw struct { 1011 jsonext.RawMessage 1012 } 1013 1014 func (m Raw) Validate(formats strfmt.Registry) error { 1015 var f interface{} = m.RawMessage 1016 if v, ok := f.(runtime.Validatable); ok { 1017 return v.Validate(formats) 1018 } 1019 return nil 1020 } 1021 ``` 1022 1023 You can embed types as pointers just the same. 1024 1025 Example: 1026 ```yaml 1027 definitions: 1028 Time: 1029 type: string 1030 x-go-type: 1031 type: Time 1032 import: 1033 package: time 1034 hints: 1035 nullable: true # <- nullable here refers to the nullability of the embedded external type 1036 embedded: true 1037 ``` 1038 1039 ```go 1040 type Time struct { 1041 *time.Time 1042 } 1043 ``` 1044 1045 1046 Using external types is powerful, but normally you still have to describe your type in the specification. That is expected, 1047 since this is how you document your API. 1048 1049 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, 1050 because it simply has no way to infer what _kind_ of object is being referred to. 1051 1052 To solve this kind of problem, you may hint the generator to produce a correct usage of the external types, even though the specification 1053 doesn't reflect the correct nature of the object. 1054 1055 Example: 1056 ```yaml 1057 definitions: 1058 Error: 1059 type: object 1060 1061 Hotspot: 1062 x-go-type: 1063 type: Hotspot 1064 import: 1065 package: github.com/go-swagger/go-swagger/fixtures/enhancements/2224/external 1066 hints: 1067 kind: object 1068 x-nullable: true 1069 ``` 1070 In this example, the `Hotspot` schema is empty in the specification. The generator therefore can only guess that this is some `interface{}` type. 1071 Now thanks to the hint `kind: object`, we instruct the generator to expect an object so as to correctly reference this object. 1072 1073 1074 ###### Validation of external types 1075 1076 By default, the generator assumes that external types can be validated and will generate code that calls the "Validate" 1077 method of the type. 1078 1079 This can be disabled by providing an explicit hint: 1080 ```yaml 1081 MyObject: 1082 type: object 1083 properties: 1084 p1: 1085 x-go-type: 1086 type: RawMessage 1087 import: 1088 package: encoding/json 1089 hints: 1090 noValidation: true 1091 ``` 1092 1093 External types with an hint type "interface" or "stream" do not call validations. 1094 1095 Embedded types use type assertion to dynamically determine if the external type implements the `runtime.Validatable` interface. 1096 1097 1098 ###### External type hints 1099 1100 The generator does not attempt to introspect external types. They may even not exist at generation time. 1101 1102 Therefore, the generator has no idea of whether it is safe to generate pointers to the external type. 1103 1104 By default, external types are considered non nullable. This can be altered with the nullable hint or 1105 by hinting a type that is considered nullable (such as "object"). 1106 1107 Supported hints: 1108 ```yaml 1109 x-go-type: 1110 type: {external type name (exported symbol, without package qualifier)} 1111 import: 1112 package: {fully qualified package name - defaults to the target models defined by the --model-package flag} 1113 hints: 1114 kind: {map|object|array|interface|primitive|tuple|stream} 1115 noValidation: true|false # <- skips validation: defaults to true for embedded types, defaults to false for non-embedded, always false for kinds interface and stream 1116 nullable: true|false # <- default to true for kinds object,primitive and tuple 1117 embedded: true|false # <- defaults to false, generates a struct that wraps the external type 1118 ``` 1119 1120 ###### Caveats with imports 1121 1122 At this moment, external packages and aliases are deconflicted against other known imports and variables. 1123 1124 Example: 1125 ```yaml 1126 MyObject: 1127 type: object 1128 properties: 1129 p1: 1130 x-go-type: 1131 type: RawMessage 1132 import: 1133 package: encoding/json 1134 hints: 1135 kind: interface 1136 ``` 1137 1138 will generate an import deconflicted against the standard lib import: 1139 ```go 1140 import( 1141 ... 1142 jsonext "encoding/json" 1143 ... 1144 ) 1145 ``` 1146 1147 ###### External package aliasing 1148 1149 External packages may be imported with an alias, like so: 1150 1151 ```yaml 1152 parameters: 1153 in: body 1154 schema: 1155 type: object 1156 x-go-type: 1157 type: MyExternalStruct 1158 import: 1159 package: github.com/example/models/custom 1160 alias: fred 1161 ``` 1162 1163 Imports will look like so: 1164 ```go 1165 import ( 1166 fred "github.com/example/models/custom" 1167 ) 1168 ... 1169 ``` 1170 1171 Some deconfliction with other known imports is applied automatically. Automatic deconfliction is not perfect, though. 1172 1173 For example: 1174 ```yaml 1175 MyObject: 1176 type: object 1177 properties: 1178 p1: 1179 x-go-type: 1180 type: RawMessage 1181 import: 1182 package: encoding/json 1183 hints: 1184 kind: interface 1185 ``` 1186 1187 ```go 1188 import ( 1189 jsonext "encoding/json" 1190 ) 1191 ``` 1192 1193 Package aliases may still conflict with packages produces by operation tags or other external imports. 1194 1195 In such cases, modify the type alias under `x-go-type` to resolve the conflict manually. 1196 1197 ### Customizing struct tags 1198 1199 When a model struct is generated, tags for json are generated to keep the original name: 1200 1201 ```go 1202 type ObjectWithTag struct { 1203 StandardTag string `json:"standardTag,omitempty"` 1204 } 1205 ``` 1206 1207 #### Extra tags 1208 1209 Extra tags may be defined with the CLI generation option `--sruct-tags`. 1210 1211 Extra tags essentially repeat the name of the field can be added from the command line option. 1212 1213 > **NOTE**: at this moment, all tag modifiers (omitempty, string) are repeated like for the json tag. 1214 1215 ```bash 1216 swagger generate model ... --struct-tags yaml,db 1217 ``` 1218 1219 ```go 1220 type ObjectWithTag struct { 1221 StandardTag string `json:"standardTag,omitempty" yaml:"standardTag,omitempty" db:"standardTag,omitempty"` 1222 } 1223 ``` 1224 1225 #### Custom tags 1226 1227 A custom may be added to a field using the `x-go-custom-tag` extension. Like so: 1228 1229 #### Omit empty values 1230 1231 By default, a struct field is omitted when it holds the zero value (tag modifier: `omitempty`). 1232 1233 Required fields are never omitted. 1234 1235 ```go 1236 type ObjectWithTag struct { 1237 RequiredField *string `json:"requiredField"` 1238 } 1239 ``` 1240 1241 This property can be altered using the `x-omitempty` extension. Like so: 1242 ```yaml 1243 objectWithTag: 1244 type: object 1245 properties: 1246 field: 1247 type: string 1248 x-omitempty: false 1249 ``` 1250 1251 ```go 1252 type ObjectWithTag struct { 1253 Field string `json:"field"` 1254 } 1255 ``` 1256 1257 The extension does not force a required field to get the "omitempty" modifier. 1258 1259 #### Numerical values as string 1260 1261 For some specific requirements, the standard json library may consider numbers as strings. 1262 This is done by adding the modifier `json:"...,string"` to the tag. 1263 1264 With go-swagger you can specify this modifier by adding the `x-go-json-string: true` extension to your type. 1265 1266 ```go 1267 type ObjectWithTag struct { 1268 NumericField int `json:"field,omitempty,string"` 1269 } 1270 ``` 1271 1272 #### XML tags 1273 1274 The XML name and attribute Swagger properties are used to generate extra tags. 1275 ```yaml 1276 definitions: 1277 objectWithXML: 1278 type: object 1279 properties: 1280 field: 1281 type: string 1282 xml: 1283 name: xmlObject 1284 attribute: true 1285 ``` 1286 1287 ```go 1288 type ObjectWithXML struct { 1289 Field string `json:"field,omitempty" xml:"xmlObject,attr,omitempty"` 1290 } 1291 ``` 1292 1293 #### The example tag 1294 1295 If you add `example` to the list of generated tags from the CLI (`swagger generate ... --struct-tags example`), 1296 a special example tag is created with the example value taken from the specification. 1297 1298 ```yaml 1299 definitions: 1300 objectWithExample: 1301 properties: 1302 field: 1303 type: string 1304 example: "sample" 1305 ``` 1306 1307 ```go 1308 type ObjectWithExample struct { 1309 Field string `json:"field,omitempty" example:"\"sample\""` 1310 } 1311 ``` 1312 1313 #### The description tag 1314 1315 If you add `description` to the list of generated tags from the CLI (`swagger generate ... --struct-tags description`), 1316 a special description tag is created with the description value taken from the specification. 1317 1318 ```yaml 1319 definitions: 1320 objectWithDescription: 1321 properties: 1322 field: 1323 type: string 1324 description: "some description" 1325 ``` 1326 1327 ```go 1328 type ObjectWithDescription struct { 1329 Field string `json:"field,omitempty" description:"\"some description\""` 1330 } 1331 ``` 1332 1333 1334 [swagger]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#schema-object 1335 [strfmt]: https://github.com/go-openapi/strfmt 1336 [runtime]: https://github.com/go-openapi/runtime 1337 [File]: https://github.com/go-openapi/runtime/blob/master/file.go#L20 1338 [Validatable]: https://github.com/go-openapi/runtime/blob/master/interfaces.go#L101 1339 [validate]: https://github.com/go-openapi/validate 1340 [validate-json]: https://godoc.org/github.com/go-openapi/validate#ex-AgainstSchema 1341 [go-doc-model]: https://godoc.org/github.com/go-swagger/go-swagger/examples/generated/models 1342 [read-only]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#fixed-fields-13 1343 [all-formats]: https://github.com/go-openapi/strfmt/blob/master/README.md 1344 [easy-json]: https://github.com/mailru/easyjson 1345 [json-schema]: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 1346 [lifting-pointers]: https://github.com/go-swagger/go-swagger/pull/557