I am trying to document with OpenAPI a query string which look like
filtered[0][id]=code&filtered[0][value]=12345
and contains a list of object with properties id and value.
My yaml documentation looks like the following
parameters:
- name: filtered
in: query
description: filters to be applied
explode: true
style: deepObject
schema:
type: array
items:
properties:
id:
description: name of the field to be filtered
type: string
value:
description: value of the filter
type: object
The problem is the following: it looks like the style: deepObject option works only for one level, and not at the second level where my objects actually are. That is, it expects a query string like
?sorted[0]=%7B%0A%20%20%22id%22%3A%20%22string%22%2C%0A%20%20%22value%22%3A%20true%0A%7D
with the object not serialized as an array with id and value keys.
Is there a way to solve this?
This is not possible as of OpenAPI 3.1
OpenAPI 3.0/3.1 Specifications currently defines the deepObject behavior only for simple objects (with primitive properties) such as
{
"id": 5,
"name": "Bob"
}
but not for arrays and not for nested objects.
Since the behavior for arrays and nested objects is not defined, there's really no way to describe your query string. Technically, the only way would be to define filtered[0][id], filtered[0][value], etc. as individual query parameters.
If you are designing a new API (rather than describing an existing one), consider passing the array of objects in the request body instead.
As name implies, :), deepObject style only "provides a simple way of rendering nested objects", not arrays. At the least according to Version 3.0.1 it applies only to objects.
Note that even nested objects might be not yet supported by tools because the specification "does not provide such examples".
So your format is not compatible with Open API, yet may be you can define you query as parameters which follow a regex. I such cases usually I do my best yet provide some explanation
https://swagger.io/specification/
Update. Apparently, after somebody demanded a 'descriptive error' message for nested objects, a patch (PR) to support nested objects was suggested at https://github.com/swagger-api/swagger-js/pull/1450 . Regretfully it was decided not to incorporate this feature, and PR 1450 was rejected.
I know it's an old question, but starting open api 3 there is a support for what you looking for
https://swagger.io/docs/specification/describing-parameters/#schema-vs-content.
it was reported as a bug that was fixed with openapi generator 6.0.1 +
https://github.com/OpenAPITools/openapi-generator/issues/4808
parameters:
- in: query
name: filter
content:
application/json:
schema:
schema:
$ref: "#/components/schemas/Filter"
Related
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.
The json data structure for jstree is define in https://github.com/vakata/jstree, here is an example
[ { "text" : "Root node", "children" : [ "Child node 1", "Child node 2" ] } ]
Notably it says
The children key can be used to add children to the branch, it should
be an array
However later on in section Populating the tree using AJAX and lazy loading nodes it shows to use set children to false to indicate when a child has not be processed
[{
"id":1,"text":"Root node","children":[
{"id":2,"text":"Child node 1","children":true},
{"id":3,"text":"Child node 2"}
]
}]
So here we see children used as both as an array and as a boolean
I am using jstree as an example because this is where I encountered the issue, but my question is really a general json question. My question is this, is it valid JSON for the same element in json to be two different types (an array and a boolean)
Structure wise, both are valid JSON packets. This is okay, as JSON is somewhat less stricter than XML(with a XSD or a DTD). As per: https://www.w3schools.com/js/js_json_objects.asp,
JSON objects are surrounded by curly braces {}.
JSON objects are written in key/value pairs.
Keys must be strings, and values must be a valid JSON data type (string, number, object, array, boolean or null).
Keys and values are separated by a colon.
Each key/value pair is separated by a comma.
Having said that, if the sender is allowed to send such JSONs, only caveat is that server side will have to handle this discrepancy upon receiving such different packets. This is a bad-looking-contract, and hence server might need to do extra work to manage it. Server side handling of such incoming JSON packets can become tricky.
See: How do I create JSON data structure when element can be different types in for use by
You could validate whether a JSON is okay or not at https://jsonlint.com/
See more about JSON in this answer: https://stackoverflow.com/a/4862511/945214
It is valid Json. JSON RFC 8259 defines a general syntax but it contains nothing that would allow a tool to identify that two equally named entries are meant to describe the same conceptual thing.
The need to have a criteria to check two JSON structures for instance equality has been one motivation to create something like Json Schema.
I also think it is not too unusual for javascript to provide this kind of mixed data. Sometimes it might help to explicitly convert the javascript object to JSON. Like in JSON.stringify(testObject)
A thing for json validation
https://www.npmjs.com/package/json-validation
https://davidwalsh.name/json-validation.
I really like the concept of using failed tests to ensure the documentation is up to date. But I don't know how to make it work for nested json. The way Spring REST Docs handles hierarchical payload seems to defeat the purpose:
When documenting fields, the test will fail if an undocumented field is found in the payload. Similarly, the test will also fail if a documented field is not found in the payload and the field has not been marked as optional. For payloads with a hierarchical structure, documenting a field is sufficient for all of its descendants to also be treated as having been documented.
How would you write your tests for nested json so changes to the payload result in a failed test?
Example:
{
car: {
motor : {
brand: "Porsche",
power: "165 kW"
},
suspension: {
type: "automatic"
}
}
Test:
.andDo(document("mytest", responseFields(
fieldWithPath("car").description("the car").type(JsonFieldType.OBJECT),
fieldWithPath("car.motor").description("the motor").type(JsonFieldType.OBJECT),
fieldWithPath("car.motor.brand").description("the motor brand").type(JsonFieldType.STRING),
fieldWithPath("car.suspension").description("the suspension"))))
A test with these response field definitions would pass even though car.motor.power and suspension.type are not defined. Is there a way to make it work? Multiple tests?
The intent was to allow people to document all of the fields if they wanted, without forcing them to do so. However, as you have observed, it can lead to a new field in the API being missed. With hindsight, this was probably a mistake.
One way to avoid missing a new field is to only document "leaf" fields. In your example that would be:
car.motor.brand
car.motor.power
suspension.type
You could do this in a separate test if you also wanted to keep the more detailed documentation. Another alternative would be to use something like JsonPath to assert the structure of the payload.
I realise that none of these is ideal so I've opened https://github.com/spring-projects/spring-restdocs/issues/274.
I got an anonymous array which I want to deserialize, here the example of the first array object
[
{ "time":"08:55:54",
"date":"2016-05-27",
"timestamp":1464332154807,
"level":3,
"message":"registerResourcePath ('', '/sap/bc/ui5_ui5/ui2/ushell/resources/')",
"details":"","component":"sap.ui.ModuleSystem"},
{"time":"08:55:54","date":"2016-05-27","timestamp":1464332154808,"level":3,"message":"URL prefixes set to:","details":"","component":"sap.ui.ModuleSystem"},
{"time":"08:55:54","date":"2016-05-27","timestamp":1464332154808,"level":3,"message":" (default) : /sap/bc/ui5_ui5/ui2/ushell/resources/","details":"","component":"sap.ui.ModuleSystem"}
]
I tried deserializing using CL_TREX_JSON_SERIALIZER, but it is corrupt and does not work with my JSON, here is why
Then I tried /UI2/CL_JSON, but it needs a "structure" that perfectly fits the object given by the JSON Object. "Structure" means in my case an internal table of objects with the attributes time, date, timestamp, level, messageanddetails. And there was the problem: it does not properly handle references and uses class description to describe the field assigned to the field-symbol. Since I can not have a list of objects but only a list of references to objects that solution also doesn't works.
As a third attempt I tried with the CALL TRANSFORMATION as described by Horst Keller, but with this method I was not able to read in an anonymous array, and here is why
My major points:
I do not want to change the JSON, since that is what I get from sap.ui.log
I prefere to use built-in functionality and not a thirdparty framework
Your problem comes out not from the anonymity of array, but from the awkwardness of SAP JSON (De)serializer, which doesn't respect double quotes, which enclose JSON attributes. The issue is thoroughly described in this answer.
If you don't want to change your JSON on-the-fly, the only way you have is to change CL_TREX_JSON_DESERIALIZER class like this.
/UI5/CL_JSON_PARSER parses JSONs with unknown format.
Note that it's got "for internal use" written on it so many times that you probably should take it seriously and clone its code to fixate it.
I imagine there must me a solution for this, I haven't found one however.
I receive JSON responses of this structure:
{
description: SomeString,
type: ACTIVITY,
timestamp: 1224043200000,
creationTime: 1224043200000,
userIdentification: 1111-2222-3333,
value: 2000
}
"Activity" is the name of my CoreData entity. How do I map this JSON to my data model? All examples I found so far assume a JSON similar to this format:
{
activity: {
description: SomeString,
timestamp: 1224043200000,
creationTime: 1224043200000,
userIdentification: 1111-2222-3333,
value: 2000
}
}
I'm assuming that you have multiple possible different types, each with an associated entity...
Define a mapping for each entity. Don't connect these directly to the response descriptors. Instead, create an RKDynamicMapping instance with a block (setObjectMappingForRepresentationBlock:, or a matcher) which checks the type of the data coming in and returns the appropriate mapping.
If your responses contain multiple different types in a single response then you may want to look at combining the above with KVC validation to reject any objects created with the wrong type. You would also need some different response descriptors or something to apply each of the different entity mappings...
You can look in the ObjectMapping section of the RestKit repo on github. To map your json response you can make use of the nil keyPath as explained here https://github.com/RestKit/RestKit/wiki/Object-mapping#mapping-values-without-key-paths.
As Wain said, you should use Dynamic Mapping to decide which class will be mapped with the json object that you are receiving depending on the value that certain attribute have. How to use Dynamic Mapping is explained here: https://github.com/RestKit/RestKit/wiki/Object-mapping#dynamic-object-mapping. I know it's late, but this could help somebody in the future.