问题
I have a model defined as:
Event:
type: object
properties:
id:
type: string
timestamp:
type: string
format: date-time
type:
type: string
enum:
- click
- open
- sent
click:
type: object
properties:
url:
type: string
title:
type: string
open:
type: object
properties:
subject:
type: string
sent:
type: object
properties:
subject:
type: string
from:
type: string
to:
type: string
However, the shape is actually different depending on the value of the type
enum field. It can be one of three, I tried to define the differences in the examples
(see below) but this did not work.
As you can see, the below shows three examples, each one is different depending on the type
. This is how the API behaves, but it is not straightforward to document this expected behaviour.
examples:
application/json:
- id: >
ad1b12f0-63a8-47b5-9820-3e447143ce7e
timestamp: >
2017-09-29T16:45:20.000+00:00
type: click
click:
url: >
www.some-website-somewhere.org/12345
title: >
Click here!
- id: >
d9e787fa-db49-4bd8-97c3-df45f159295c
timestamp: >
2017-09-29T16:45:20.000+00:00
type: open
open:
subject: >
Have you seen this?
- id: >
30adc194-0f5f-4889-abd4-4fa964d0bb42
timestamp: >
2017-09-29T16:45:20.000+00:00
type: sent
sent:
subject: >
Have you seen this?
from: >
bill@office.gov
to: >
mon@gmail.com
I read a few places that mention this is not possible, but I am hoping to get some "outside the box" thinkers to help me find a solution.
回答1:
This is possible using model inheritance/composition and discriminator.
First, define the base model Event
that contains just the common properties, and mark the type
property as discriminator
:
definitions:
Event:
type: object
discriminator: type
required:
- type # discriminator property is required
properties:
id:
type: string
example: ad1b12f0-63a8-47b5-9820-3e447143ce7e
timestamp:
type: string
format: date-time
example: 2017-09-29T16:45:20.000+00:00
type:
type: string
enum:
- click
- open
- sent
Then inherit the click/open/sent models from the base Event
model via allOf
. The names of the child models must be the same as the values of the discriminator property. In your example, the models must be named click
, open
and sent
. If you use Swagger UI, you can add title
to override the displayed model names.
click:
title: ClickEvent
allOf:
- $ref: '#/definitions/Event'
- type: object
properties:
click:
type: object
properties:
url:
type: string
example: www.some-website-somewhere.org/12345
title:
type: string
example: Click here!
In operations, use the base model (Event
) as the request or response schema:
responses:
200:
description: OK
schema:
$ref: '#/definitions/Event'
The clients should examine the value of the discriminator property (in this example - type
) to decide which inherited model to use.
Note for Swagger UI users: Swagger UI and Swagger Editor currently do not support switching models based on discriminator
. Follow this issue for updates.
In OpenAPI 3.0 it's easier - you can simply use oneOf
to define alternate schemas for a request or response.
responses:
'200':
description: OK
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/ClickEvent'
- $ref: '#/components/schemas/OpenEvent'
- $ref: '#/components/schemas/SentEvent'
# Optional mapping of `type` values to models
discriminator:
propertyName: type
mapping:
click: '#/components/schemas/ClickEvent'
open: '#/components/schemas/OpenEvent'
sent: '#/components/schemas/SentEvent'
来源:https://stackoverflow.com/questions/46557096/swagger-variant-schema-shape-dependant-on-field-value