@@ -20,17 +20,12 @@ For example, an API **should not** use a completely generic field (such as
2020correspond to one of a known number of schemas. Instead, the API **should** use
2121a [`oneof`](./oneof) to represent the known schemas.
2222
23- ### Generic fields in protobuf APIs
23+ ### Oneof
2424
25- #### Oneof
26-
27- A `oneof` **may** be used to introduce a type union: the user or API is able to
28- specify one of the fields inside the `oneof`. Additionally, a `oneof` **may**
29- be used with the same type (usually strings) to represent a semantic difference
30- between the options.
31-
32- Because the individual fields in the `oneof` have different keys, a developer
33- can programmatically determine which (if any) of the fields is populated.
25+ A `oneof` (proto) or `oneOf` (OpenAPI) **may** be used to introduce a type
26+ union: the user or API is able to specify one of the fields or schemas.
27+ Additionally, it **may** be used with the same type (usually strings) to
28+ represent a semantic difference between the options.
3429
3530A `oneof` preserves the largest degree of type safety and semantic meaning for
3631each option, and APIs **should** generally prefer them over other generic or
@@ -39,11 +34,51 @@ when there is a large (or unlimited) number of potential options, or when there
3934is a large resource structure that would require a long series of "cascading
4035oneofs".
4136
42- **Note:** Adding additional possible fields to an existing `oneof` is a
43- non-breaking change, but moving existing fields into or out of a `oneof` is
44- breaking (it creates a backwards-incompatible change in Go protobuf stubs).
37+ **Note:** Adding additional fields or schemas to an existing `oneof` is a
38+ non-breaking change. However, removing or moving fields is breaking.
4539
46- #### Maps
40+ {% tab proto %}
41+
42+ Because the individual fields in the `oneof` have different keys, a developer
43+ can programmatically determine which (if any) of the fields is populated.
44+
45+ Moving existing fields into or out of a `oneof` creates a
46+ backwards-incompatible change in Go protobuf stubs.
47+
48+ {% tab oas %}
49+
50+ ```yaml
51+ components:
52+ schemas:
53+ PaymentMethod:
54+ type: object
55+ properties:
56+ payment:
57+ oneOf:
58+ - type: object
59+ properties:
60+ creditCard:
61+ type: string
62+ description: Credit card token
63+ - type: object
64+ properties:
65+ bankAccount:
66+ type: string
67+ description: Bank account identifier
68+ - type: object
69+ properties:
70+ giftCard:
71+ type: string
72+ description: Gift card code
73+ ```
74+
75+ Because the individual schemas in the `oneOf` can be distinguished (through
76+ discriminators or schema validation), a developer can programmatically
77+ determine which (if any) of the schemas matches the provided value.
78+
79+ {% endtabs %}
80+
81+ ### Maps
4782
4883Maps **may** be used in situations where many values _of the same type_ are
4984needed, but the keys are unknown or user-determined.
@@ -54,25 +89,77 @@ sometimes be suited to a situation where many objects of the same type are
5489needed, with different behavior based on the names of their keys (for example,
5590using keys as environment names).
5691
57- #### Struct
92+ {% tab proto %}
93+
94+ {% tab oas %}
95+
96+ ```yaml
97+ components:
98+ schemas:
99+ Configuration:
100+ type: object
101+ properties:
102+ environments:
103+ type: object
104+ additionalProperties:
105+ type: object
106+ properties:
107+ replicas:
108+ type: integer
109+ region:
110+ type: string
111+ ```
112+
113+ Maps are represented using `additionalProperties` in OpenAPI.
58114
59- The [`google.protobuf.Struct`][struct] object **may** be used to represent
60- arbitrary nested JSON. Keys can be strings, and values can be floats, strings,
61- booleans, arrays, or additional nested structs, allowing for an arbitrarily
62- nested structure that can be represented as JSON (and is automatically
63- represented as JSON when using REST/JSON).
115+ {% endtabs %}
64116
65- A `Struct` is most useful when the API does not know the schema in advance, or
66- when a API needs to store and retrieve arbitrary but structured user data.
67- Using a `Struct` is convenient for users in this case because they can easily
68- get JSON objects that can be natively manipulated in their environment of
69- choice.
117+ ### Free-form objects
70118
71- If a API needs to reason about the _schema_ of a `Struct`, it **should** use
72- [JSONSchema][] for this purpose. Because JSONSchema is itself JSON, a valid
73- JSONSchema document can itself be stored in a `Struct`.
119+ Free-form objects **may** be used to represent arbitrary nested JSON. Keys can
120+ be strings, and values can be numbers, strings, booleans, arrays, or additional
121+ nested objects, allowing for an arbitrarily nested structure that is
122+ represented as JSON.
74123
75- #### Any
124+ A free-form object is most useful when the API does not know the schema in
125+ advance, or when a API needs to store and retrieve arbitrary but structured
126+ user data. Using a free-form object is convenient for users in this case
127+ because they can easily get JSON objects that can be natively manipulated in
128+ their environment of choice.
129+
130+ If a API needs to reason about the _schema_ of a free-form object, it
131+ **should** use [JSONSchema][] for this purpose. Because JSONSchema is itself
132+ JSON, a valid JSONSchema document can itself be stored in a free-form object.
133+
134+ {% tab proto %}
135+
136+ The [`google.protobuf.Struct`][struct] object represents arbitrary nested JSON,
137+ and is automatically represented as JSON when using REST/JSON.
138+
139+ {% tab oas %}
140+
141+ ```yaml
142+ components:
143+ schemas:
144+ CustomResource:
145+ type: object
146+ properties:
147+ name:
148+ type: string
149+ metadata:
150+ type: object
151+ description: Arbitrary structured data
152+ additionalProperties: true
153+ ```
154+
155+ Free-form objects are represented using `type: object` with
156+ `additionalProperties: true` or without defined properties.
157+
158+ {% endtabs %}
159+
160+ ### Any
161+
162+ {% tab proto %}
76163
77164The [`google.protobuf.Any`][any] object can be used to send an arbitrary
78165serialized protocol buffer and a type definition.
@@ -83,12 +170,22 @@ the proto. Additionally, even if the consumer _does_ have the proto, the
83170consumer has to ensure the type is registered and then deserialize manually,
84171which is an often-unfamiliar process.
85172
86- Because of this, `Any` **should not** be used unless other options are
87- infeasible.
173+ {% tab oas %}
174+
175+ ```yaml
176+ components:
177+ schemas:
178+ GenericValue:
179+ type: object
180+ properties:
181+ data: {} # Empty schema - accepts any value
182+ ```
88183
89- ### Generic fields in OAS APIs
184+ An empty schema (represented as `{}`) accepts any valid JSON value, including
185+ primitives, objects, and arrays. This is the OpenAPI equivalent of
186+ `google.protobuf.Any`.
90187
91- **Note:** OAS-specific guidance not yet written.
188+ {% endtabs %}
92189
93190<!-- prettier-ignore-start -->
94191[any]: https://github.com/protocolbuffers/protobuf/tree/master/src/google/protobuf/any.proto
0 commit comments