Jersey Rest Request Generic type - java

I am having the trouble to sent the generic json request from client to server. Here is my service and pojo classes. Can some correct me where i am doing wrong.
Service :
#POST
#Path("/executeApp")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Response executeApp(Configuration configuration) {
}
Config class: This class consist list of Tasks,Task is abstract class is below.
#XmlRootElement
public class Configuration{
public Configuration() {
// TODO Auto-generated constructor stub
}
public String udid;
public boolean overrideExistingTask;
public int repeatPeriodMinutes;
public List<? extends Task> tasks;
//Getters and setters
}
Task class:
public abstract class Task {
public Task(){
}
public String testId;
public int pauseSeconds;
public int cycles;-
public abstract String getFriendlyName();
protected abstract Task<?> getTaskInstance();
//Getters and setters
}
Derived classes XXXX,YYYYY both extends Task:
public class XXXXX extends Task{
String friendlyName = this.getClass().getSimpleName();
//Extra varibles
public XXXXX() {
super();
}
#Override
public String getFriendlyName() {
return friendlyName;
}
#Override
protected Task<?> getTaskInstance() {
}
}
Client postman request with json:
{
"udid":"123",
"overrideExistingTask":"true",
"repeatPeriodMinutes":"2",
"tasks":[{"XXXXX":{"testId":"testId", "pauseSeconds" :"5", "cycles":"2" ,"requestId":"123" ,"url":"url", "port":"port" , "udid":"udid" }}]
}
error:
javax.ws.rs.ProcessingException: Error deserializing object from entity stream.</p>
The server encountered an unexpected condition that prevented it from fulfilling the request.
javax.servlet.ServletException: javax.ws.rs.ProcessingException: Error deserializing object from entity stream.
If i pass empty array instead of XXXXX, I am able to see the values of Configuration class(udid,overrideExistingTask,repeatPeriodMinutes) in debug mode but taskes values coming []. User can pass list of XXXXX ,YYYYY or both. Can some one suggest me where a doing wrong or missing rest annotation to define generic type.

The Configuration object will not be able to parse the provided JSON input.
The tasks fields in the Configuration is a list of tasks. In the JSON provided, it is a List of Map(/Object) with key "XXXXX" and value is the Task Object.
If this is the JSON to be used, then the tasks field in the Configuration object should be modified to
public List<Map<String,<? extends Task>> tasks;
or
The JSON input should be modified to send a list of tasks.
{
"udid": "123",
"overrideExistingTask": "true",
"repeatPeriodMinutes": "2",
"tasks": [{
"testId": "testId",
"pauseSeconds": "5",
"cycles": "2",
"requestId": "123",
"url": "url",
"port": "port",
"udid": "udid"
}
//More task objects here
]
}

Related

Nested JSON request - PostMapping in Springboot

I need to create a post request in Springboot and call an API to update records. The mapping JSON layout is a nested one as mentioned below :
{
"Config": {
"Identity": "",
"Region": ""
},
"OPTION_IP_COLL": {
"OPTION_NAME": "",
"OPTION_VALUE": ""
},
"REC_XPFH": {
"UPDATE_CD": "",
"ENTITY_TYPE": "",
"TN_ID": "",
"PR_ID": ""
},
"REC_GRP": {
"REC_PRPR": {
"PR_UPDATE_CD": "",
"PR_ENTITY": "",
"PR_CL_EFT_IND": "",
"PR_EDI_DEST_ID": "",
"PR_RA_DEST_IND": ""
},
"REC_MCBR": {
"BD_ID": "",
"BR_ACCT_NO": "",
"BR_ACCT_NAME": "",
"BR_ACCT_NO_QUA": ""
}
}
}
As this is a nested one, I am bit confused how to define a POJO(it should be single or multiple) for this request and call it in #PostMapping. I am thinking to create one Main POJO and then different classes for other segments and then call the Main one in the RestController class. I am not sure whether this is a right way to do it or is there something else that I could try. Also, I need some assistance to define REC_GRP class as there are nested segments involved.
MainPOJO.class
public class ProviderMappingPOJO {
private Config config;
private OPTIONAL_IP_CALL option_ip_call;
private REC_XPFH rec_xpfh ;
private REC_GRP rec_grp;
//getter and setter method .....
Config.class
private String Identity ;
private String Region;
// getters and setters method...
OPTION_IP_COLL.class
private String UPDATE_CD;
private String ENTITY_TYPE;
private String TN_ID;
private String PR_ID;
// getter and setter methods ...
REC_XPFH.class
// Not sure how to define structure for this as there are nested segments
RestController.class
public class RestController {
#PostMapping(url)
public <> getProviderDetails(#RequestBody MainPOJO main){
// code
}
You can use one main pojo and inside that main pojo, you can define child pojos as you are mentioned above.
public class MainPOJO {
private Config config;
private OPTIONAL_IP_CALL option_ip_call;
private REC_XPFH rec_xpfh ;
private REC_GRP rec_grp;
// setters and getters
}
You can refer the below links, which will helps you to get a clear understanding about the conversion process.
https://dzone.com/articles/converting-json-to-pojos-using-java
http://www.jsonschema2pojo.org/
https://www.freecodeformat.com/json2pojo.php
Once you completed the conversion, you can use the Spring Rest controller to consume the json.
public class RestController {
#PostMapping(path="url", consumes = "application/json")
public <> getProviderDetails(#RequestBody MainPOJO main){
// code
}
}

Can't map second level json value with Spring Webflux Webclient

I am consuming the public API for crypto currencies in Mexico: https://api.bitso.com/v3/available_books/ that returns a json like this one:
"success": true,
"payload": [
{
"book": "btc_mxn",
"minimum_price": "500.00",
"maximum_price": "16000000.00",
"minimum_amount": "0.000075",
"maximum_amount": "500.00000000",
"minimum_value": "5",
"maximum_value": "10000000.00"
},
{
"book": "eth_btc",
"minimum_price": "0.00000100",
"maximum_price": "5000.00000000",
"minimum_amount": "0.00000100",
"maximum_amount": "1000.00000000",
"minimum_value": "0.00000100",
"maximum_value": "2000.00000000"
},
and the code that consumes it with Webclient is:
#Override
public Mono<Coins> getCoins() {
return webClient.get().uri("https://api.bitso.com/v3/available_books/")
.accept(MediaType.APPLICATION_JSON)
.retrieve().bodyToMono(Coins.class);
}
The POJOs that are trying to bind it are:
#Data
public class Coins {
#JsonProperty("success")
private String success;
#JsonProperty("playload")
private List<Coin> playload;
and
#Data
public class Coin {
#JsonProperty("book")
private String book;
#JsonProperty("minimum_amount")
private String minimumAmount;
#JsonProperty("maximum_amount")
private String maximumAmount;
#JsonProperty("minimum_price")
private String minimumPrice;
#JsonProperty("maximum_price")
private String maximumPrice;
#JsonProperty("minimum_value")
private String minimumValue;
#JsonProperty("maximum_value")
private String maximumValue;
So far, it only maps like this
"success": true,
"payload": null
You need to have no-args construct and change the word playload to payload :)
I don't think this is a WebFlux issue, but rather a Jackson + Lombok issue.
What happens if you try to deserialize that payload with raw ObjectMapper?
I think Jackson requires an all args constructor annotated with #JsonCreator or ask Lombok to create a #NoArgConstructor for that class. In any case, rewriting your Coin class as a regular Java class should work.
Also, your Coins class has a typo since it's trying to get playload instead of payload.
FIXED: Typo at property name playload instead of payload

Jackson JsonMappingException Error - Multiple fields representing property 'PROPERTY_NAME'

I have a class where I have multiple field named results, (Actually I have 12 of them, but for the sake of the question, I just include 2 in this question)
public class APIRequest {
#JsonProperty("code")
public String code;
#JsonProperty("error")
public APIError error;
#JsonProperty("results")
public APILogin login;
#JsonProperty("results")
public APIUser user;
}
The reason I have this because my backend API call will always return the results field for every request
for example http://api.testapp.com/get_user_profile would return this JSON
The results key would then be mapped by APIUser class
{
"code": "200",
"results": {
"name": "Jackson Liu"
"age": "21"
"first_name": "Jackson"
"last_name": "Liu"
}
}
And then http://api.testapp.com/login would return this JSON
The results key would then be mapped by APILogin class
{
"code": "200",
"results": {
"token": "12u3912edsdnisnknaklsmdlsadmsalma"
"session_id": "ladlwjopwjwpdmdlndlkadlaasassa"
"state": "1"
}
}
And because of that, I will get this error.
com.fasterxml.jackson.databind.JsonMappingException: Multiple fields
representing property "results":
id.testapp.android.testapp.jsonobjects.APIResults#login vs
id.testapp.android.testapp.jsonobjects.APIResults#user
Any thoughts on how should I fix this?
To make it simple use MAP. Jackson will take care of populating MAP. Just provide setter and getter for each field. And Depends on your context you can read required field in Map
public class APIRequest {
#JsonProperty("code")
public String code;
#JsonProperty("error")
public APIError error;
#JsonProperty("results")
Map<String, String> results;
}

Jackson Jersey JSON

I'm trying to use Jersey and Jackson (although any other way of doing JSON demarshalling works) to get this into my system in some form (be it POJO or some other representation).
Basically I only need the data section. I was trying to use GenericTypes with lists, but this is a nested list and I'm just not sure what to do. Lots of kudos for help and I really appreciate it!
{
"total": 4,
"data": [
{
"descriptor": "",
"multiInstance": false,
"active": false
},
{
"descriptor": "Apparel",
"multiInstance": true,
},
{
"descriptor": "abcd123",
"multiInstance": false,
},
{
"descriptor": "abcd",
"multiInstance": false,
}
]
}
This is the class I'm trying to use. I need a list of the class.
public class customObject {
#JsonProperty(value = "descriptor")
private String descriptor;
#JsonProperty(value = "multiInstance")
private Boolean multiInstance;
//getters and setters
}
Edit:
I'm using it in here.
CustomObjectResponse WDCOResponse =
resource
.type(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header("Authorization", getToken()).get(WDCOResponse.class);
But it's still not working.
Edit2:
Figured this out! Thanks to everyone. :)
I had to add annotation to tell it to ignore if something wasn't found, some of the JSON I'm getting back was not fully-formed in that not all fields were absolutely neccesary.
If you the object you provided is what you are passing to your controller, then you will need one more wrapper object to contain the list like this:
public class CustomRequest {
#JSonProperty(value = "total");
private Integer total;
#JsonProperty(value = "data")
private List<CustomObject> data;
// getters/setters
}
public class CustomObject {
#JsonProperty(value = "descriptor")
private String descriptor;
#JsonProperty(value = "multiInstance")
private Boolean multiInstance;
// getters/setters
}
Then your controller will just have annotations that show that the RequestBody is the CustomRequest class:
#Controller
public class JSONController {
#RequestMapping(value="sendData")
public #ResponseBody CustomResponse sendData(
#RequestBody CustomRequest request)
{
return null;
}
}
If you are still getting errors, please provide detailed error or problem. Thanks!
You'd use POJO like:
public class Response {
int count;
List<customObject> data;
}
and access the data from there:
for (customObject ob : response.data) {
// process ig
}

Jackson: Serializing/Deserializing String to Array with single entry

I am currently writing a Java client that consumes a RESTful service, using the Restlet package and its Jackson extension.
I want to query the service for a user and deserialize the response to a User POJO that looks as follows:
#JsonIgnoreProperties(ignoreUnknown=true)
public class User {
private Integer uid;
private String name;
private String mail;
private String field_nickname;
// omitted for brevity: getters/setters, toString
}
A sample response from the service looks as follows:
{
"uid": "5",
"name": "John Doe",
"mail": "john#example.com",
"field_nickname": {
"und": [{
"value": "jdoe",
"format": null,
"safe_value": "jdoe"
}]
}
}
Here is the Java client code:
import org.restlet.resource.ClientResource;
public class TestClient {
public static void main(String[] args) throws Exception {
// Getting a User
ClientResource cr = new ClientResource("http://localhost/rest/user/7.json")
User user = cr.get(User.class);
System.out.println(user);
// Creating a User
cr = new ClientResource("http://localhost/rest/user.json");
User user = new User();
user.setName("Jane Doe");
user.setFieldNick("jdoe2");
user.setMail("jdoe2#example.com");
cr.post(user);
}
The serialization/deserialization of the uid, name and mail fields is very straightforward and poses no problems.
My problem is with field_nickname: The field always contains the array und with a single entry that always looks the same.
How can I tell Jackson to deserialize this field to a String that holds the value of field_nickname[und][0][value] and serialize the attribute into such an array?
This is kind of one-off structural transformation that you will need to write your own handler. The easiest way is probably just to implement something like:
#JsonProperty("field_nickname")
public void setNickname(NicknameWrapper[] wrapper) {
field_nickname = wrapper[0].value;
}
#JsonIgnoreProperties(ignoreUnknown=true)
static class NicknameWrapper {
public String value;
}
and perhaps reverse (getNickname()) as well.

Categories