Is there a way to present the request body of a complex object in swagger with each field having it's input?
In simple words if one of my apis expects a Person (suppose it has just a firstname/lastname) as #RequestBody then the only way to provide this Person with swagger would be to give the entire json of Person. Is there a way to enable each separate field to have it's separate input for firstname/lastname for example?
If you annotate your Operation using #ModelAttributeit should do exactly what you're looking for
For e.g. instead of
public void updatePersonName(#RequestBody Person person) { ... }
Use this
public void updatePersonName(#ModelAttribute Person person) { ... }
The #ModelAttribute will expand the primitive properties and provide fields for entering in the first name and last name in the swagger ui. The equivalent of that operation is
public void updatePersonName(#RequestParam String firstName,
#RequestParam String lastName) { ... }
I'm also using swagger with the default swagger UI and I don't think it's possible unless you change the swagger UI code. But a quite convenient solution is to define the type of your parameter and then provide the model for that type. For instance
"parameters": [
{
"in": "body",
"name": "body",
"description": "A Person",
"required": true,
"type": "Person"
}
]
And then
"definitions": {
"Person": {
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
}
}
}
}
This way the model appears in the UI and you can easily copy it to the request body textbox and fill it with content manually.
Related
I am creating a Rest service which calls an external service.
I have this problem: the external service I call responds in two ways depending on the case of Success or Failed. This is json:
json{
"result": "001",
"status": "Success",
"response": {
"codiceCase": "CAS-46759-Q8P7X3",
"guidCase": "88458d32-dd42-ec11-8c62-0022489d2f61"}
}
OR
{
"result": "002",
"status": "Failed",
"errorManagement": {
"errorCode": "E02",
"errorDescription": "field not value in body"
}
}
Well I created 3 simple classes:
class XXX... private String result, status; .... getter & setter
class Response... private String codiceCase, guidCase; ... getter & setter
class ErrorManagement...private String errorCode, errorDescription;... getter & setter
But when I populate with my mock, the json is always formed with the class field that I don't care for example:
{
"result": "001",
"status": "Success",
"response": {
"codiceCase": "CAS-46759-Q8P7X3",
"guidCase": "88458d32-dd42-ec11-8c62-0022489d2f61" },
{
***"errorMessage"**: null}
}
How can I get only 2 of the 3 classes returned as json in my mock?
Thanks for your help.
I suppose you’re using Jackson to deserialize JSON as this is - I think - the default for spring.
With Jackson you can annotate the model class to omit null values in the JSON string representation:
#JsonInclude(Include.NON_NULL)
public class Model { … }
I have got two main model classes: Customer and Product
public class Customer {
String name;
String surname;
int age;
BigDecimal cash;
}
public class Product {
String name;
Category category;
BigDecimal price;
}
I want to build json file with Map<Customer, List<Product>>
When I write to json file data with my method which works correct - I am sure about this - the json file shows this syntax
{
"Customer{name\u003d\u0027Custo1\u0027, surname\u003d\u0027Surname\u0027, age\u003d18, cash\u003d1200}": [
{
"name": "prod1",
"category": "CLOTHES",
"price": 12000
},
{
"name": "prod2",
"category": "ELECTRONIC",
"price": 15000
}
]
}
Then when i want to read this file, the error Exception in thread "main" java.util.NoSuchElementException: No value present occurs so I think that the Customer syntax from json file is not recognized.
So I tried to write data to json file on my own with this syntax below, but it does not work
[
{
"name": "Abc",
"surname": "Def",
"age": 14,
"cash": "2000"
}
:
[
{
"name": "prod1",
"category": "CLOTHES",
"price": 12000
},
{
"name": "prod2",
"category": "ELECTRONIC",
"price": 15000
}
]
]
json converter method:
public void toJson(final T item) {
try (FileWriter fileWriter = new FileWriter(jsonFilename)) {
fileWriter.write(gson.toJson(item));
} catch (Exception e) {
throw new ValidatorException(e.getMessage());
}
}
#Tom is right on the issues you've faced with. I'll explain why and suggest one more solution.
Your first JSON is technically a valid JSON but it cannot be deserialized, because the map keys are results of the Customer.toString() method Gson uses by default. This is why it looks weird, acts like a debug string, and can't be deserialized back: there it is almost always no way to restore an object from the toString() result (toString is designed mostly for debugging/logging purposes providing basic information regarding the state of a particular object that does not need to expose its all internals at all).
Your second JSON is invalid JSON. Period.
Tom's suggestion of making the list of products a part of the customer class is totally fine. Having it implemented like that lets you to serialize everything as a list like this:
[
{
"name": "john",
"products": [
{"name": "prod1"},
{"name": "prod2"}
]
}
]
Hint: separating domain objects (Customer and Product) and representation objects for data transfer (CustomerDto and ProductDto) is usually a fine idea too since it allows to create representation for any concrete representation implementation (one for various JSON implementation libraries, two for other-format-oriented tools, third for persistence, four for UI views, etc), so it might be implemented like converting Map<Customer, List<Product>> to List<CustomerDto> and back (possibly by using mapper-generators like MapStruct).
If for whatever reason it is not possible to reorganize your domain classes or create Gson-friendly DTO-mappings, or you're fine to keep it as simple as possible and you're fine with having not that trivial JSON structure (as long as you understand implications of the format in this solution: evolution, distribution, etc), then you can enable special Gson mode to support this kind of maps. It generates valid JSONs that can be serialized and deserialized back, but the way it is implemented looks a bit of anti-pattern to me because of losing semantics due to using arrays as the data container.
#AllArgsConstructor
#EqualsAndHashCode
#ToString
final class Customer {
final String name;
}
#AllArgsConstructor
#EqualsAndHashCode
#ToString
final class Product {
final String name;
}
public final class MapTest {
private static final Gson gson = new GsonBuilder()
.enableComplexMapKeySerialization()
.create();
private static final TypeToken<Map<Customer, List<Product>>> customerToProducts = new TypeToken<Map<Customer, List<Product>>>() {};
#Test
public void test() {
final Map<Customer, List<Product>> ordersBefore = ImmutableMap.of(
new Customer("john"), ImmutableList.of(new Product("prod1"), new Product("prod2"))
);
final String json = gson.toJson(ordersBefore, customerToProducts.getType());
Assertions.assertEquals("[[{\"name\":\"john\"},[{\"name\":\"prod1\"},{\"name\":\"prod2\"}]]]", json);
final Map<Customer, List<Product>> ordersAfter = gson.fromJson(json, customerToProducts.getType());
Assertions.assertEquals(ordersBefore, ordersAfter);
}
}
Note that it generates JSON like this (index 0 means the key, index 1 means the value):
[
[
{"name": "john"},
[
{"name": "prod1"},
{"name": "prod2"}
]
]
]
So i am trying to build a json to send data to the body of my restassured request, like this structure here:
{
"id": 1,
"category": {
"id": 1,
"name": "duch"
},
"name": "benny",
"photoUrls": [
"string"
],
"tags": [
{
"id": 0,
"name": "string"
}
],
"status": "available"
}
So it is as simple as to copy this as string to the body of the request and i am done, i don't want that at all.
Is there a framework of sorts to give this structure and to change the data dynamically somehow?
I don't want this: (for example)
given().body("{\r\n\"city\": \"Hod Hasharon\",\r\n\"description\": \"Automation Hotel\",\r\n\"name\":\"Nir Great hotel\",\r\n\"rating\":5\r\n}")
.when().post("http://localhost:8090/example/v1/hotels").then().statusCode(201);
I want to be more flexible here, to reference some kind of object (A template with the option to change the data in some places?) that handles this stuff, is there something like that?
I think what you need is using POJO and Jackson to serialize it to json.
public class Payload {
private int id;
private String name;
private List<Tag> tags; //Tag is another class you need to create the same way
//getters, setters
}
And then using objects as payload in your request:
Payload payload = new Payload();
payload.setId(123);
payload.setName("John");
given().contentType("application/json").body(payload).when().post("http://example.com");
Also don't forget to add jackson-databind dependency to your project.
There's more about that in official documentation here: https://github.com/rest-assured/rest-assured/wiki/Usage#object-mapping
I have been looking solution for this problem but could not find one so asking this question.
I have some data which looks like this
{
"data": [
{
"id": "5ab892c71810e201e81b9d39",
"isSignedUpUsingFb": false,
"personalInformation": {
"firstName": "jio",
"lastName": "g",
"mobileNumber": "1234567890",
},
"accountBalance": 0,
}
]
},
I want to write a java code to change the data structure to this
{
"data": [
{
"id": "5ab892c71810e201e81b9d39",
"isSignedUpUsingFb": false,
"personalInformation_firstName":"jio",
"personalInformation_lastNAme":"g",
"personalInformation_mobileNumber":"1234567890",
"accountBalance": 0,
}
]
},
I am getting data from db as:
#Override
public List<User> getAllUsers() {
logger.debug("entering all users method");
List<User> allUsers=mongoOperations.findAll(User.class);
for (User user : allUsers) {
PersonalInformation info=user.getPersonalInformation());
//manipulation code here
user.setPersonalInformation(info);
}
return allUsers;
}
So I want to write a logic so that i can convert the data in desired format and send it a return type. I know how to do same thing using J query but I want to do it in backend so any code for the above or any link will help.
I have fond one solution which is very simple.So, basically when we create object for nested data we create it like this in JAVA.
public MyClass{
public String name;
public String contact;
public PersonalInformation personalinformation;
//setters and getter here
}
this will give me data as
"MyClass":{
"name": "abc",
"contact": "12345",
"personalInformation":{
"address": "asdasdasdad",
"city":"asdadad",
"pin": "asdfg",
}
}
so to remove this nested data we need to use #JsonUnwrapped which removes all the nested object and add it to our main object.
public MyClass{
public String name;
public String contact;
#JsonUnwrapped
public PersonalInformation personalinformation;
//setters and getter here
}
which will change the data structure as:
"MyClass":{
"name": "abc",
"contact": "12345",
"address": "asdasdasdad",
"city":"asdadad",
"pin": "asdfg",
}
for more reference you can check this link http://fasterxml.github.io/jackson-annotations/javadoc/2.0.0/com/fasterxml/jackson/annotation/JsonUnwrapped.html
Hope this helps.
There are multiple possible solutions. As Prabhav has mentioned the most intuitive one would be to create a new class and from there a object which can be transformed with a library to a JSON.
Variant one:
The new class would look like your data structure you want and access would be:
PersonalInformationJson pf = new PersonalInformationJson();
pf.setFirstName = info.getPersonalInformation_firstName
//... setting the rest of the object
//using jackson
ObjectMapper mapper = new ObjectMapper();
try {
// convert user object to json string and return it
String jsonString = mapper.writeValueAsString(u);
}
The other easier version to create a string, either per hand or use a lib:
// using org.json.JSONObject
String jsonString = new JSONObject().put("personalInformation_firstName", info.value())
.put("personalInformation_lastNAme", info.value());
Here is the sample JSON I want to deserialize with Jackson.
{
"person": {
"contacts": {
"address": {
"type": "Office",
"street": "1600 Amphitheatre Parkway",
"city": "Mountain View",
"state": "CA",
"zip": "94043",
"country": "United States"
},
"email": {
"type": "Home",
"emailAddress": "e.schmidt#google.com"
},
"phone": [
{
"type": "Mobile",
"phoneNumber": "+1 888 555555"
},
{
"type": "Home",
"phoneNumber": "+1 888 1111111"
}
],
"website": {
"type": "work",
"webService": "URL",
"webAddress": "www.google.com"
}
},
"firstName": "Eric",
"lastName": "Schmidt"
}
}
The tricky bit to deserialize here is the contacts node.
Things to note:
contacts is a polymorphic abstract type (see POJOs below)
the type information (e.g., `addresss) is contained as a key in a wrapper
this wrapper can be an object if there is only one value (email, address, website) OR an array if there are multiple (phone)
Target POJOs:
public class Person
{
public String firstName;
public String LastName;
public List<Contact> contacts; // mixes Address, Phone, Email, Website
}
public abstract class Contact {
public Long id;
}
public class Phone extends Contact
{
public String type;
public String phoneNumber;
}
// other subtypes of Contact omitted for brevity
note: external requirements require that I use the abstract Contact type. I would rather deserialize directly to these POJOs rather than having an intermediate Contacts POJO that the contact types hang off of and them manual mapping/converting to my List in another step.
I've looked over many other jackson + polymorphic deserialization questions, but none seem to handle this case (#2 and #3 in particular).
I want to deserialize the contacts object to a List<Contact>.
What is proper application of #JsonTypeInfo and #JsonSubTypes needed to achieve this?
(if anyone is interested this is CapsuleCRM's JSON format)
You'll need to write a custom deserializer and register it with Jackson. The implementation would check for initial start then parse accordingly. Think sax style processing.