I want to define an object with a key and a value but can't figure out how to define it as a property in a swagger 2.0 definition. Frankly, I am not sure if this is even possible if one wants to be able to define strongly typed clients. However, I figure I'd ask to see if this is even possible/allowable.
Basically, I want to be able to store the following object:
{ "currencies":
{ "usd" : 1.10,
"eur" : 2.25
}
}
But I don't want usd, and eur to have to be defined in the schema. I can do the below but I don't want usd or eur to be predefined. I want it to accept any value for the keys of the object.
"Product": {
"properties": {
"currencies": {
"type": "currencyObj"
},
}
},
},
"currencyObj": {
"id": "currencyObj",
"properties": {
"eur": {
"type": "float"
},
"usd": {
"type": "float"
}
}
}
Swagger 2.0 allows you to define a string to type mapping, you need to use additionalProperties. Use following notation to define a map :
"currencyObj": {
"type": "object",
"additionalProperties": {
"type": "number"
}
}
Related
I need to deserialize ( and later on serialize ) a piece of data that has this type of a structure :
{
"type": "TypeApplication",
"val": {
"con": {
"type": "TypeConstructor",
"val": [
"Builtin",
"Record"
]
},
"arg": {
"type": "RowCons",
"val": {
"label": "953e3dd6-826e-1985-cb99-fd4ed4da754e",
"type": {
"type": "TypeApplication",
"val": {
"con": {
"type": "TypeConstructor",
"val": [
"Builtin",
"List"
]
},
"arg": {
"type": "Element",
"meta": {
"multiLine": true
}
}
},
"system": {
"label": "nullam-senectus-port - Text",
"isBindable": true,
"defaultValue": [
{
"id": "4a05486f-f24d-45f8-ae13-ab05f824d74d",
"type": "String",
"pluginType": "Basic",
"data": {
"value": "Nullam senectus porttitor in eget. Eget rutrum leo interdum."
},
"children": [],
"text": true
}
],
"isUnlinked": false,
"isDefault": false
}
},
"tail": {
"type": "RowCons",
"val": {
"label": "94f603df-d585-b45a-4252-9ec77cf5b13c",
"type": {
"type": "TypeApplication",
"val": {
"con": {
"type": "TypeConstructor",
"val": [
"Builtin",
"List"
]
},
"arg": {
"type": "Element",
"meta": {
"multiLine": true
}
}
},
"system": {
"label": "best-services - Text",
"isBindable": true,
"defaultValue": [
{
"id": "6265ca45-3f69-4844-97e2-c05bbfb9fee5",
"type": "String",
"pluginType": "Basic",
"data": {
"value": "Best Services"
},
"children": [],
"text": true
}
]
}
},
"tail": {
"type": "RowEmpty"
}
}
}
}
}
}
}
I do not know what this data exactly is, but I know this is trying to represent a function/element that takes in values and defaults for those values as parameters/properties.
I want to deserialize it using serde and consequently serialize it too.
I have so far been able to write something that sort of works but not really :
#[skip_serializing_none]
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(tag = "type", content = "val")]
pub enum WebflowPropDataType {
TypeApplication {
con: Box<WebflowPropDataType>, // Normally Passes the Type Constructor
arg: Box<WebflowPropDataType>, // Normally Passes the Row Constructor
},
TypeConstructor(Vec<String>), // Stores Value of TypeConstructor
RowCons {
label: String, // Stores the label of the Row
#[serde(rename = "type")]
row_con_type: Box<WebflowPropDataType>, // Stores the type of the Row
tail: Box<WebflowPropDataType>,
},
RowEmpty, // For Ending the recursive usage of rowConstructor
}
#[skip_serializing_none]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct WebflowRowConDataType {
#[serde(rename = "type")]
val_type: String, // TypeApplication
val: Box<WebflowPropDataType>,
}
This works for a structure like this :
{
"type": "TypeApplication",
"val":{
"con": {
"type": "TypeConstructor",
"val": []
},
"arg": {
"type": "RowEmpty"
}
}
}
but would fail if I try to work with initial example. I know this may be due to the lack of a proper arg type or maybe even the TypeApplication Enum hand is malformed.
I do see that a adjancent typing solution would be enough for most of the times but there are cases as seen in the example structure that there is a third value called system and I am unable to determine what type of approach would help me achieve this type of outcome.
How should I approach this problem in order to generate this type of code.
I am not asking anyone to write me a solution but to give me suggestion as to what my approach should be? Whether you'd know what type of data this is/how to generated this , or even if there are some other library I should look into to manipulate this type of data or maybe look at other approaches.
PS : - My end goal is to be able to generate / serialize this type of JSON code from already contained knowledge of properties and default values of a function/object.
Here are my recommendations:
Use just #[serde(tag = "type")] instead of #[serde(tag = "type", content = "val")]. You will have to handle val manually (extracting the current enum members into separate structs), but this allows you to also handle TypeApplication.system and Element.meta.
This also has the small benefit of reducing the amount of Boxes involved.
Consider whether all of the different cases in WebflowPropDataType can actually occur everywhere it's used. If not (maybe Element can only happen under TypeApplication.val.arg), then you may want to split the enum into multiple so that this is reflected in the type system.
Example for #1:
use serde::{Serialize, Deserialize};
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct TypeApplicationVal {
con: WebflowPropDataType, // Normally Passes the Type Constructor
arg: WebflowPropDataType, // Normally Passes the Row Constructor
}
// #[skip_serializing_none]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct TypeApplicationSystem {
label: String,
#[serde(rename = "isBindable")]
is_bindable: bool,
// TODO: defaultValue
#[serde(rename = "isUnlinked")]
#[serde(skip_serializing_if = "Option::is_none")]
is_unlinked: Option<bool>,
#[serde(rename = "isDefault")]
#[serde(skip_serializing_if = "Option::is_none")]
is_default: Option<bool>,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct RowConsVal {
label: String, // Stores the label of the Row
#[serde(rename = "type")]
typ: WebflowPropDataType, // Stores the type of the Row
tail: WebflowPropDataType,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct ElementMeta {
#[serde(rename = "multiLine")]
multi_line: bool,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(tag = "type")]
pub enum WebflowPropDataType {
TypeApplication {
val: Box<TypeApplicationVal>,
#[serde(skip_serializing_if = "Option::is_none")]
system: Option<TypeApplicationSystem>,
},
TypeConstructor {
val: Vec<String> // Stores Value of TypeConstructor
},
RowCons {
val: Box<RowConsVal>,
},
Element {
meta: ElementMeta,
},
RowEmpty, // For Ending the recursive usage of rowConstructor
}
playground
The API I'm trying to describe has a structure where the root object can contain an arbitrary number of child objects (properties that are themselves objects). The "key", or property in the root object, is the unique identifier of the child object, and the value is the rest of the child object's data.
{
"child1": { ... bunch of stuff ... },
"child2": { ... bunch of stuff ... },
...
}
This could similarly be modeled as an array, e.g.:
[
{ "id": "child1", ... bunch of stuff ... },
{ "id": "child2", ... bunch of stuff ... },
...
]
but this both makes it structurally less clear what the identifying property is and makes uniqueness among the children's ID implicit rather than explicit, so we want to use an object, or a map.
I've seen the Swagger documentation for Model with Map/Dictionary Properties, but that doesn't adequately suit my use case. Writing something like:
"Parent": {
"additionalProperties": {
"$ref": "#/components/schemas/Child",
}
Yields something like this:
This adequately communicates the descriptiveness of the value in the property, but how do I document what the restrictions are for the "key" in the object? Ideally I'd like to say something like "it's not just any arbitrary string, it's the ID that corresponds to the child". Is this supported in any way?
Your example is correct.
how do I document what the restrictions are for the "key" in the object? Ideally I'd like to say something like "it's not just any arbitrary string, it's the ID that corresponds to the child". Is this supported in any way?
OpenAPI 3.1
OAS 3.1 fully supports JSON Schema 2020-12, including patternProperties. This keyword lets you define the format of dictionary keys by using a regular expression:
"Parent": {
"type": "object",
"patternProperties": {
"^child\d+$": {
"$ref": "#/components/schemas/Child"
}
},
"description": "A map of `Child` schemas, where the keys are IDs that correspond to the child"
}
Or, if the property names are defined by an enum, you can use propertyNames to define that enum:
"Parent": {
"type": "object",
"propertyNames": {
"enum": ["foo", "bar"]
},
"additionalProperties": {
"$ref": "#/components/schemas/Child"
}
}
OpenAPI 3.0 and 2.0
Dictionary keys are assumed to be strings, but there's no way to limit the contents/format of keys. You can document any restrictions and specifics verbally in the schema description. Adding schema examples could help illustrate what your dictionary/map might look like.
"Parent": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/Child"
},
"description": "A map of `Child` schemas, where the keys are IDs that correspond to the child",
"example": {
"child1": { ... bunch of stuff ... },
"child2": { ... bunch of stuff ... },
}
If the possible key names are known (for example, they are part of an enum), you can define your dictionary as a regular object and the keys as individual object properties:
// Keys can be: key1, key2, key3
"Parent": {
"type": "object",
"properties": {
"key1": { "$ref": "#/components/schemas/Child" },
"key2": { "$ref": "#/components/schemas/Child" },
"key3": { "$ref": "#/components/schemas/Child" }
}
}
Then you can add "additionalProperties": false to really ensure that only those keys are used.
So maybe I'm trying to go into too much detail in my descriptor, but here's my use case. While defining the schema of the object that is returned in the response body for my API, I have one property of the object the value of which is an object with variable properties, so I am using the additionalProperties descriptor.
{
"type": "object",
"additionalProperties": {
"type": "string"
}
}
However instead of describing the type of the values in this variable keyed object as string, I would like to describe them more precisely, seeing that the string is actually a pipe delimited serialization of an array. Example:
{
"property1": {
"variableProperty1": "info1|info2|info3",
"variableProperty2": "info1|info2|info3"
}
}
Is there any way of describing this pipe-delimited serialization? Something like:
{
"type": "object",
"additionalProperties": {
"type": "array",
"explode": false,
"encoding": {
"style": "pipeDelimited"
}
},
}
Or is this only possible for query parameters?
SPEC provides next example how to identify schema:
{
"$id": "http://example.com/root.json",
"definitions": {
"B": {
"$id": "other.json",
},
}
}
#/definitions/B
http://example.com/other.json
http://example.com/other.json#
http://example.com/root.json#/definitions/B
But how is it identified if root schema root.json would be defined under /some/path instead of / path?
{
"$id": "http://example.com/some/path/root.json",
"definitions": {
"B": {
"$id": "other.json",
},
}
}
How other.json should be identified?
http://example.com/other.json
or:
http://example.com/some/path/other.json
And which part of SPEC defines this?
Schemas can be identified by any URI that has been given to them,
including a JSON Pointer or their URI given directly by "$id". In all
cases, dereferencing a "$ref" reference involves first resolving its
value as a URI reference against the current base URI per RFC 3986
[RFC3986].
(Dereferencing section)[http://json-schema.org/latest/json-schema-core.html#rfc.section.8.3.2] of the spec.
The "base URI" is defined in RFC 3986 which is referenced in the JSON Schema specification.
It's not super easy to understand because it's quite complex. In the case of a URL, where the reference to resolve is is a non hash fragment, the base URI is is the URI parts before (but including) the last slash.
(Note: JSON Schema defines that the value of an $id must be an absolute URI, without any fragments.)
So to answer your question specifically other.json should be identified as http://example.com/some/path/other.json.
You can see this in action if you try using the following schema in this online JSON Schema validator...
{
"$id": "http://example.com/blah/root.json",
"definitions": {
"A": {
"$id": "#foo"
},
"B": {
"$id": "other.json",
"definitions": {
"X": {
"$id": "#bar"
},
"Y": {
"$id": "t/inner.json"
}
}
},
"C": {
"$ref": "http://example.com/blah/other.json"
}
},
"properties":{
"a": { "$ref": "#/definitions/C" }
}
}
In the $ref for "C", if you remove /blah, the validator will complain it can no longer resolve the reference.
I have a simple JSON object that can contain key/values for which the exact values are not known upfront. They depend on some server side process. How do I model this in Swagger?
An example of the JSON would be:
...
,message: "invalid length. Must be in between {min} and {max}"
,attributes: {
min: 0
,max: 6
}
...
Another example would be:
...
,message: "You must fill out this field as well because {field} has a value"
,attributes: {
field: "age"
}
...
The following solution will only work with Swagger 2.0.
Define the model as described like this:
{
"type": "object",
"properties": {
"message": {
"type": "string"
},
"attributes": {
"type": "object",
"additionalProperties": {}
}
}
}
This describes attributes as a map of properties, where the value can be anything (string, number, array and even an object).