How to create query param body with multiple key param values - java

In the below hashmap you can see, I have list of key param values for which I need to automate cases for multiple values without repeating the hashmap rather it would be and update.
How I am doing it:
1st test case
HashMap<String, String> queryParam = new HashMap<>();
queryParam.put("Name", Name);
queryParam.put("street","street" );
queryParam.put("city","city" );
queryParam.put("state", "state");
queryParam.put("postalCode","postalCode" );
queryParam.put("country", "country");
queryParam.put("email", "email");
queryParam.put("website","website" );
queryParam.put("phone", "phone");
Response response = request.auth().basic(uname, pwd).body(queryParam).contentType(APPLICATION_JSON)
.post().then().extract()
.response();
Now if you see the above hashmap, it has mandatory params, some optional and then each param has different validation. Now it terms to cover all the testcases with each keys, above haspmap is repeating and values or keys are changing. I would like to do this in better and efficient way of it.

Instead of using Map<K, V>, you should use Java POJO. Using constructor to setup default value, then using setter to change value. It's more efficient.
One more thing, you could apply Factory design pattern to build object with desired value for each test case.
Test example
#Test
void test1() {
QueryObject query = QueryObjectFactory.getDefaultValue();
Response res = given().contentType(ContentType.JSON)
.body(query)
.post("to_your_api_endpoint");
}
Factory class
public class QueryObjectFactory {
public static QueryObject getDefaultValue() {
QueryObject queryObject = new QueryObject();
queryObject.setName("name");
queryObject.setStreet("street");
queryObject.setCity("city");
queryObject.setCountry("country");
queryObject.setState("state");
queryObject.setPostalCode("postalCode");
queryObject.setEmail("email");
queryObject.setWebsite("website");
queryObject.setPhone("phone");
return queryObject;
}
}
POJO
note: I use lombok to generate getter and getter --> reduce complex of POJO class.
import lombok.Data;
#Data
public class QueryObject {
private String name;
private String street;
private String city;
private String state;
private String postalCode;
private String country;
private String email;
private String website;
private String phone;
}

Related

Mapping all request params into an object in Spring Controller [duplicate]

This question already has answers here:
In Spring-mvc the attribute names in view have to always match the property names in model?
(3 answers)
How to customize parameter names when binding Spring MVC command objects?
(10 answers)
Closed 3 years ago.
So, url requested looks like
localhost:8080/contacts?id=22&name=John&eventId=11
and also I got an object to map request into
public class ContactDTO {
private Long id;
private String name;
private Long eventId;
}
I use a controller method like passing my request params into an object
#GetMapping("/contacts")
public ContactDTO contacts(ContactDTO contact) {
// everything is awesome! contact maps clearly
return contact;
}
The question is how to map like this but have different name
localhost:8080/contacts?id=22&name=John&event_id=11
Setting #JsonAttribute doesn't works because Jackson mapper works only in requestbody.
Maybe I should write custom HandlerMethodArgumentResolver or something like that?
P.S.
I've got a dirty hack (objectMapper is injected, so I can use #JsonAttributes),
But this case fails on array mapping, same mapping with requestbody works fine
#GetMapping("/contacts")
public ContactsDTO contacts(#RequestParam Map<String,String> params) {
ContactDTO contactDTO = objectMapper.convertValue(params,ContactDTO.class);
return contactDTO;
}
Since it is an API design requirement, it should be clearly reflected in the corresponding DTO's and endpoints.
Usually, this kind of requirement stems from a parallel change and implies that the old type queries will be disabled during the contract phase.
You could approach the requirement by adding the required mapping "query-parameter-name-to-property-name" by adding it to the ContactDTO. The simplest way would be just to add an additional setter like below
public class ContactDTO {
private Long id;
private String name;
private Long eventId;
public void setEvent_id(Long eventId) {
this.eventId = eventId;
}
}
If you prefer immutable DTO's, then providing a proper constructor should work as well
#Value
public class ContactDTO {
private Long id;
private String name;
private Long eventId;
public ContactDTO(Long id, String name, String eventId, String event_id) {
this.id = id;
this.name = name;
this.eventId = eventId != null ? eventId : event_id;
}
}
Use something like
#RequestParam(name="event_id", required = true) long eventId
in the parameter list to change the parameter name.
Use #RequestBody insteaf of #requestparam.

How to map a Java Map to Object using Orika

I am trying to map a Java Map to a POJO but having problems with using Orika.
I have a LinkedHashMap and trying to map it to a POJO. I've been reading this website https://www.baeldung.com/orika-mapping, specifically section 4.2
This is how I define my orika mapper:
factory.classMap(Map.class, TestDto.class)
.field("nest['name']", "name")
.toClassMap();
and this is the LinkedHashMap I'm trying to map:
Map<Object, Object> nest = new LinkedHashMap<>();
nest.put("name", "myname");
Map<Object, Object> obj = new LinkedHashMap<>();
obj.put("nest", nest);
and this is the POJO I'm trying to map to:
public class TestDto {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
when I do:
TestDto testDto = mapper.map(obj, TestDto.class);
I get null as name. Is this scenario possible to map or do I have to do more customization of orika?
Yeah the name will return null for this mapping.
TestDto testDto = mapper.map(obj, TestDto.class);
This is due to variable name in TestDto class does not contain variable name nest.
To map your POJO with different key.
Pleas add #JsonProperty in your get() method.
Example:
#JsonProperty("nest")
public String getName() { return name; }
I figured it out, you can define a mapper like:
factory.classMap(Map.class, TestDto.class)
.field("nest:{get('nest')|type=Map}.name:{get('name')|type=java.lang.String}", "name")
.toClassMap();
which is outlined here:
https://orika-mapper.github.io/orika-docs/advanced-mappings.html
This is really only needed if you don't have control over the Pojo and/or do not want to make a copy of the Pojo

Serialize a class to json in diffrent ways

Is there any way to serialize a class into json but only with fields I want to use in particular case without need of creating multiple variations of class?
Let's take an example:
class User{
#JsonField
private String name;
#JsonField
private String surname;
#JsonField
private String hashedCode;
#JsonField
private String city;
#JsonField
private String anotherDummyString;
}
Now in one of my methods I would like to have a mapping to json Object which only contains name, city and anotherDummyString.
In Second method I want to have surname and city. In third method ... .
Is there any pleasant and neat way to achive this?
I was wondering if I can "hide" these fields which I don't need.
Are you looking for something like this? (Using javax.json)
JsonObject getJson1() {
return Json.createObjectBuilder().add("name", this.name).add("city", this.city).build();
}
JsonObject getJson2() {
return Json.createObjectBuilder().add("surname", this.surname).add("city", this.city).build();
}
Just .add what you need in each function call.
I hope you are looking for a kind of filter for your fields in Class
This can be achieved using Jackson #JsonFilter
package com.concretepage;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonFilter("student")
public class Student
{
#JsonProperty("name")
private String name;
#JsonProperty("surname")
private String surname;
#JsonProperty("hashedCode")
private String hashedCode;
#JsonProperty("city")
private String city;
#JsonProperty("anotherDummyString")
private String anotherDummyString;
}
Create a simple filter for you above class
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("student",
SimpleBeanPropertyFilter.serializeAllExcept("name", "city","anotherDummyString"));
Set it to a object mapper
ObjectMapper mapper = new ObjectMapper();
mapper.setFilterProvider(filterProvider);
To get the json message
//Pass the student object
String jsonData = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(student);
You must able to have you 2 variance of class by creating one more sample filter like above

Maintaining ordering of Class fields during Json Deserialization

Lets say I have a class as follows:
#JsonIgnoreProperties(ignoreUnknown = true)
class MyClass {
#JsonProperty(value="vertical")
private String vertical;
private String groupId;
#JsonProperty(value = "relationships")
private void unwrapGroupId(Map<String, Map<String, List<Map<String, Object>>>> relationships) {
this.groupId = ""; // Some logic to process the relationships map & set this.groupId based on the value set in this.vertical during deserialization
}
}
When deserializing an API Response to MyClass, is it guaranteed that vertical field is set before unwrapGroupId() is processed???? Else my processing in unwrapGroupId() would fail as this.vertical will be empty. If not , how can it be achieved.
I looked up #JsonPropertyOrder, but looks like it doesnt solve this usecase
Note: I use Jackson 2.8.1

Play Framework - How to map web data to object data

Imagine the following scenario:
I send a request to a service (which uses Play framework) with the following parameters (parameter's name should be underscored by convention):
first_name=James&second_name=Parker
Moreover I have a model class in my codebase which looks like this.
public class User {
#Constraints.Required
private String firstName;
#Constraints.Required
private String secondName;
public String getFirstName() {
return firstName;
}
public String getSecondName() {
return secondName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setSecondName(String secondName) {
this.secondName = secondName;
}
}
All I want to do is to map parameter's names with the corresponding
field names. The following statement using Play Framework fails because
User object cannot be initialized with the given parameters of request.
Form<User> form = Form.form(User.class).bindFromRequest();
Read this first: https://www.playframework.com/documentation/2.1.1/JavaRouting
And then:
GET /myMethod/:firstName/:seccondName controllers.MyController.myMethod(firstName: String, seccondName: String)
and simple Method:
public myMethod(String firstName, String lastName) {
User u = new User();
u.setFirstName(firstName);
u.setSeccondName(seccondName);
}
Obviously if you use play framework 2.
You have underscores in the request parameter names, yet your class has the members in camelCase (e.g. first_name vs fistName [Missing a r here]).
Secondly it also seems like these class members are declared private AND you have no setter methods only getters (i.e. setFirstName).
In order to do the binding your class is instantiated and the values are set and with no way to do so it will fail.
Once you've fixed the above, you should be able to bind and then call form.hasErrors() to check if validation has failed.
Hope that helps.
Update:
To handle the mismatch between the request parameter names and the class member names you would probably have to manually set things up.
In your controller method you would do something like this:
Map<String, String[]> reqBody = request().body().asFormUrlEncoded()
Map<String, String[]> newReqBody = new HashMap<>();
for(Map.Entry<String, String[]> entry: body.entrySet()) {
newBody.put(underscoreToCamelCase(entry.key()), entry.value());
}
Form<User> form = Form.form(User.class).bindFromRequest(newReqBody);
Note that I'm using the overloaded version of bindFromRequest as seen here
You should then implement the underscoreToCamelCase method in a generic enough way to handle all your conventions (Perhaps you might have a situation where there are more than one underscores).

Categories

Resources