In the OpenAPI docs about parameter serialization there's a short section about how to serialize query, path, header and cookie parameters with different styles. The schema of these parameters are described as OpenAPI flavoured json schema, which allows infinite nesting of objects and arrays. I haven't found any mention about how to deal with these in the docs:
https://swagger.io/docs/specification/serialization/
Let's assume the JSON schema provided for any of the parameters is like this:
{
"type": "object",
"properties": {
"foo": {
"type": "object",
"properties": {
"bar": "string"
}
}
}
}
Meaning it allows structures in JSON such as:
{
"foo": {
"bar": "hello"
}
}
Or similar concept with arrays that are nested:
{
"type": "array",
"items": {
"type": "array",
"items": {
"type": "string"
}
}
}
Which allows structures like this (at least in JSON):
[["a"], ["b"]]
My question:
Is this allowed for path, query, etc parameters according to OpenAPI spec?
In case it is, are there any docs on how to serialize these in the different styles the spec allows?
In case it is not, is this mentioned anywhere in official docs?
I'm asking this because I'm working on tooling that needs to be compatible with the OpenAPI spec, and I'd like to know what can I expect here as parameter formats. I'm fully aware that having giant nested objects and trying to serialize them in a url is not the smartest idea. However I'm interested in what the OpenAPI spec allows.
Short answer: It's undefined behavior.
Most OpenAPI serialization styles are based on RFC 6570, which provides guidance only for:
primitive values,
arrays of primitives,
simple non-nested objects (with primitive properties).
In case of other types of values (nested objects, objects containing arrays, nested arrays, arrays of objects) the behavior is undefined.
Similarly, OpenAPI's own deepObject style is currently defined only for simple objects but not for arrays or nested objects. Here are some related comments from the OpenAPI Specification authors/maintainers:
By the way, is there a reason we couldn't have deepObject work for arrays too? [...]
Darrel: Supporting arrays as you describe was my intent. I was supposed to find some canonical implementation to use as a guideline for the behavior, but didn't get around to it.
Ron: If we end up supporting the exploded array notation, it needs to be clear that the first index is 0 (or 1, or -1, or whatever).
(source)
Ron: when we defined deepObject in the spec, we explicitly chose to not mention what happens when the object has several levels in it, but in our conversations we went with 'not supported'.
(source)
There's an existing feature request to extend deepObject to support arrays and nested structures:
Support deep objects for query parameters with deepObject style
OpenAPI spec 3.0/3.1 supports parameter serialization in JSON format. It's just mentioned at the end of the page you referred.
Other Serialization Methods
schema vs content
From OpenAPI spec 3.0.3:
For more complex scenarios, the content property can define the media
type and schema of the parameter. A parameter MUST contain either a
schema property, or a content property, but not both.
Below OpenAPI snippet shows how to define the query parameters that have the desired structure in the question:
parameters:
- name: objParam
in: query
content:
application/json:
schema:
type: object
properties:
foo:
type: object
properties:
bar:
type: string
- name: nestedArray
in: query
content:
application/json:
schema:
type: array
items:
type: array
items:
type: string
Related
To be able to deserialize polymorphic types, I use a type discriminator across many of my JSON objects. E.g., { "$type": "SomeType", "otherProperties": "..." }
For the JSON schemas of concrete types, I specify a const value for type.
{
"type": "object",
"properties": {
"$type": { "const": "SomeType" },
"otherProperties": { "type": "string" }
}
}
This works, but distributes the chosen "$type" property name throughout many different JSON schemas. In fact, we are considering renaming it to "__type" to play more nicely with BSON.
Could I have prevented having to rename this property in all affected schemas?
I tried searching for a way to load the property name from elsewhere. As far as I can tell $ref only works for property values.
JSON Schema has no ability to dynamically load in key values from other location like you are asking. Specifically because the value will be different, and you want only the key to be loaded from elsewhere.
While you can't do this with JSON Schema, you could use a templating tool such as Jsonnet. I've seen this work well at scale.
This would require you have a pre-processing step, but it sounds like that's something you're planning for already, creating some sort of pipeline to generate your schemas.
A word of warning, watch out for existing schema generation tooling. It is often only good for scaffolding, and requires lots of modifications. It sounds like you're building your own, which is likely a better approach.
I have an API that returns rows of data based on a request. The rows are JSON elements containing name:value pairs. However, the attribute names (and datatypes of the values) need to be fluid and undefined in the spec.
For example:
{
"row_id": 1234,
"data": {
"foo": "bar",
"date": "2019-07-31",
"some_number": 5
}
}
In this example, the attributes 'row_id' and 'data' are the only fixed things. All the name:values pairs inside the data element can be anything.
I believe I can use open api additionalProperties to describe this, but no examples I can find tell me how or confirm that this is correct.
Does anyone have an idea how to do this or can point me in the right direction?
It looks like what I was looking for was:
recordData:
type: object
additionalProperties: {}
type: object defines it as a general object, and additionalProperties: {} says the object contains properties that have not been specifically defined.
The docs I finally located also say that additionalProperties: true would also work.
I'm trying to figure out how to achieve for Swagger to parse an associative array with an undefined number of values.
I have similar cases, with the difference that these other cases are totally regular (I know in advance all the properties' names). However, I might not know (actually I don't want to know) which could be the name of the values in these case.
Example of JSON that has an associative array with an undefined number of language codes. Each language code key, has an array of undefined translations, with a key and a value for each translations. "DESCRIPTION" and "ACCESS_ON_FOOT" in this case are the keys of the translations. But there could be others and I don't want to know all of them. It's supposed to be dynamic.
{
"ca": {
"DESCRIPTION": "Catalan description",
"ACCESS_ON_FOOT": "Catalan Access on foot"
},
"en": {
"DESCRIPTION": "English Description",
"ACCESS_ON_FOOT": "English Access on foot"
},
"es": {
"DESCRIPTION": "Spanish Description",
"ACCESS_ON_FOOT": "Spanish Access on foot"
}
}
The thing is that I don't know how to specify this example of undefined language codes as the values of an object.
I my other case, I made it work easily, since I knew which values I had. I of course could add "ca", "en" and "es" as properties of type array. But if I add languages, I should come back to the Swagger spec and make it again, and my idea is to make the process of adding a new language totally decoupled of the API spec.
Is there a way of defining in Swagger 2.0 an undetermined set of properties?
UPDATE
It seems this could be a possible solution to the issue I'm having to parse this JSON:
Translations:
type: "object"
additionalProperties:
type: object
additionalProperties:
type: string
The additionalProperties option seems to be the right one for associative arrays. At least according to the specification:
https://swagger.io/docs/specification/data-models/dictionaries/
The Swagger Editor (https://editor.swagger.io/) is showing to me the same JSON format I described, but I still get an empty output in the generated client.
After doing more research, it seems unclear to me that associative arrays are working in Swagger 2.0. I'd love to make this work. At the end I have opted to go for a different alternative, and made my API endpoint return a slightly different result.
I had an associative array with 2 dimensions and the value stored.
Now I have a unidimensional array with 3 properties:
Translations:
type: "object"
properties:
language_code:
type: "string"
type:
type: "string"
content:
type: "string"
As Translations is in the definitions set, I can easily include an array of Translations in the response of the endpoint like that:
responses:
200:
description: "successful operation"
schema:
type: "array"
items:
$ref: "#/definitions/Translations"
This worked like a charm.
Swagger seems to work very nicely, as long as you don't have cases where there is too much freedom. When that happens, you'd need to change your mind and find a different alternative to do things.
P.S. Start first with your API definition, (like you would do with tests and then code). So first API definition, then tests and then code.
I want to serialize a JSON object that has a potentially variable number of keys, representing cell phone deviceid's (due to Android and iPhones differences). The JSON object might look like this for example (for Android):
"deviceids":{
"openudid":"",
"androidid":"dcbfXXXXXc2d5f",
"imei":"3533XXXXX941712"
}
whereas an iPhone looks like this:
"deviceids":
{
"openudid":"37368a5361XXXXXXXXXXdaedc186b4acf4cd4",
"ifv":"BD87ECBF-XXXXXXXXXX-DDF46E18129A",
"mac":"XXXXXXXXXX",
"odin":"2f5672cXXXXXXXXXX2022a5349939a2d7b952",
"ifa":"82F7B2AXXXXXXXXXX5-A2DADA99D05B"
}
In Avro, I was thinking a schema like this could account for the differences:
{
"name":"deviceids",
"type":"record",
"fields":[
{
"type":"array",
"items":{
"type":"map",
"values":"string"
}
}
]
}
Is this valid Avro schema?
Yes, a map is a valid type for an array. Your particular schema is not legal however, as it should be
{
"name":"deviceids",
"type":"record",
"fields":[
{ "name": "arrayOfMaps",
"type":{
"type": "array",
"items":{
"type":"map",
"values":"string"
}
}
}
]
}
That is, the fields of your record must be named, and the type definition for array and map both have to be full definition giving both the outer complex type (map/array) and contained type.
Since it can be hard sometimes to answer specific Avro questions based on the available documentation and repository of examples, the easiest way to answer this sort of question is probably to just try to compile it using the Avro tools jar, which can be found alongside the regular jars in the Avro releases.
java -jar avro-tools-1.7.5.jar compile schema /path/to/schema .
This will quickly resolve the concern over whether or not it is valid. If this still doesn't resolve the issue, the Avro mailing lists seem fairly active.
Im trying to use json-schema validation at some project, and want to ask about the "required" field.
In current doc there is a specifiaction:
The value of this keyword MUST be an array. This array MUST have at least one element. Elements of this array MUST be strings, and MUST be unique.
But in another examples in the web, i can find something like:
"properties": {
"foo": {
"required": true
}
}
What is a valid way to define required fields?
In version 3 of JSON Schema it was a boolean.
In the latest version, 4, it is now an array of strings.
The validator you are using may still be implementing the old specification.