How to create generic json response Object in java? - java

I am new to java and creating some Restful services in netbeans using jersey framework.
I have created many GET, POST web services which is having different different type of responses which are basically Model Objects and depending upon media type I am getting JSON or XML.
Some response is having only a single object which is being shown in JSON inside {}, and some are list which are in [].
I want to see a generic response format for all api responses.
Example-
{"status":"0 or 1", "message":"any string message","result":"it can be a single object or a list of objects which is dynamic depending upon each web service response"}.
Since here in java we need to created model objects for getting responses so I have created a ResponseModel which is having a status property , message property but don't know the type of result property because sometime it can have a single object or sometime a list so what type should I set for this property so that I can assign any thing to this and response will always be in same format for JSON or XML.
I have created a static method with constructor which takes all these three parameters and create a ResponseModel object.
Thanks in advance
EDITED- Code after Using "Object" as generic type
public static Response getResponseObjectIsValid(boolean isValid, String message,Object result)
{
if (isValid == true) {
LGSResponse response = new LGSResponse();
response.setStatus(1);
response.setMessage(message.length()>0 ? message : "Success");
response.setResult(result);
return Response.status(Status.OK).entity(response).build();
}
else
{
LGSResponse response = new LGSResponse();
response.setStatus(1);
response.setMessage(message.length()>0 ? message : "Failed");
response.setResult(result);
return Response.status(Status.OK).entity(response).build();
}
}
Result Parameter is a normal model object.

You can simply create your class as you've said.
public class ResponseModel {
int status;
String message;
Object result;
// contructor, getters, setters go here...
// (probably consider using lombok, but that is a story for another time)
}
Then you can pass either a single object or an array as the 3rd param and the json/xml serializer should take care of the conversion for you unless your objects are very complex(I rarely run into that problem)
For example, your Jersey methods would look something like this:
// returning a single object within the response model
#GET
#Path("/myMethod")
public Response aJerseyMethod() {
Book aBookObject = new Book("The Title", "Name of Author");
ResponseModel responseModel = new ResponseModel(1, "This is a book", aBookObject);
return Response.ok(responseModel)
.build();
}
// returning an array within the response model
#GET
#Path("/myOtherMethod")
public Response anotherJerseyMethod() {
Book aBookObject = new Book("The Title", "Name of Author");
Book anotherBookObject = new Book("The Other Title", "Name of another Author");
ArrayList<Book> aBookArray = new Arraylist();
aBookArray.add(aBookObject);
aBookArray.add(anotherBookObject);
ResponseModel responseModel = new ResponseModel(1, "These are books", aBookArray);
return Response.ok(responseModel)
.build();
}
In both cases you should get the expected output you were talking about and you do not have to do any additional checks or conversions yourself.
I just wrote this here, so try it out with your own classes instead of "Book" (which is not a real class) and let me know if it works.

You should not maintains status code, message and result within json body.
Response message should only contain the result of that api.
Httpstatus code will be the status code (Ex.200 is success, 404 is notfound)
All the other info should be maintained in response headers, not within json body

Related

How to post JSON to Spring server?

I get a list of object from server. One key of this objects is called body, and it is a json. It is almost impossible for me to know its keys : the key name and size of it (body) are different.
Example of data
data from server = [object, object, object,.....]
object = {
id: 1,
name: "xyz",
body": {
x: "xyz",
y: "xyz",
z: "xys",
}
}
I edit the body and then post it to the server together with the id of the object. Until here it is fine. I can send the request but can not handle the Requestparam which is of type json.
How to handle this post request on the backend built in Java and Spring?
You have to add a #RequestBody annotation to the method on the Controller such as:
#RequestMapping ("url/to/save")
#ResponseBody
public ResponseObject send (#RequestBody RequestObject myRequestObject)
{
//do something
return new ResponseObject ();
}
The RequestObject would be the Java Class mapping to the JSON you want to save. The ResponseObject is whatever you want to return, you could also return void but Firefox has issues with that sometimes.

Retrofit2 handle JSON response that contains two models data

I'm using retrofit2 to handle http request after calling from API. Let me explain this.
I have 2 java class(POJO) created to handle user and lecturer data which is User.java and Lecturer.java respectively. For the response data such as :
{
"users": [
{
"user_id": "28",
"user_email": "john#abc.com",
"user_password": "123"
}
]
}
i can use User.java class to handle this response. Nothing complex in this file, only contains getter and setter method. Same goes to lecturer data, here is the example of lecturer data :
{
"lecturers": [
{
"lecturer_id": "3",
"user_id": "28",
"lecturer_name": "johny2"
}
]
}
i can handle it by using Lecturer.java class.
But the problem is, if the response contains both user and lecturer data on a single json, how to handle it?? . Here is the example of request :
{
"users": [
{
"user_id": "28",
"user_email": "john#abc.com",
"user_password": "123",
"lecturer_id": "3",
"lecturer_name": "johny2"
}
]
}
To solve this problem, i think i need to create another java class that contains both User and Lecturer class on it, unfortunately at here i'm stuck.
This is new file, that i tried to create (Userlecturer.java) :
public class UserLecturer {
User user;
Lecturer lecturer;
// how to implement on this part
}
Here is UserLecturer interface :
public interface UserLecturerInterface {
#GET ( "api/endpoint/here" )
Call<UserLecturer> getLecturerByUserId (#Path( "userId" ) String userId );
}
Appreciated for any helps. Ask me for more inputs if above use case did't clear enough. Thanks
I think the POJO should be:
public class Users {
String userId;
String userEmail;
String userPassword;
String lecturerId;
String lecturerName;
}
Even though there are 2 models inside the JSON, you only need 1 model for Retrofit.
If you really want to split the 1 JSON response into 2 models, I think you have to implement custom JSON converter.
Gson gson = new GsonBuilder()
.registerTypeAdapter(UserLecture.class, new JsonDeserializer<UserLecture>() {
public UserLecture deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonArray usersJsonArray = json.getAsJsonObject().getAsJsonArray("users");
JsonObject userJsonObject = usersJsonArray.getAsJsonArray().get(0).getAsJsonObject();
User user = new User();
user.setUserId(userJsonObject.get("user_id").getAsString());
user.setUserEmail(userJsonObject.get("user_email").getAsString());
user.setUserPassword(userJsonObject.get("user_password").getAsString());
Lecturer lecturer = new Lecturer();
lecturer.setLecturerId(userJsonObject.get("lecturer_id").getAsString());
lecturer.setLecturerName(userJsonObject.get("lecturer_name").getAsString());
return new UserLecture(lecturer, user);
}
})
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl([YOUR_BASE_URL])
.addConverterFactory(GsonFactoryConverter.create(gson))
.build();
This is some code I use to convert longs to Java Date objects.
Presumably, you can do the same thing for your UserLecture object. You should be able to extract the individual json objects for User and Lecture, create a new UserLecture object and let User and Lecture as objects in it.
Gson gson = new GsonBuilder().registerTypeAdapter(UserLecture.class, new JsonDeserializer<UserLecture>() {
public UserLecture deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject user = json.getAsJsonObject().getAsJsonObject("user");
JsonObject lecture = json.getAsJsonObject().getAsJsonObject("lecture");
return new UserLecture(user, lecture);
}
}).create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonFactoryConverter.create(gson))
.build();
Then inside UserLecture:
public UserLecture(JsonObject userJson, JsonObject lectureJson) {
this.user = new User();
this.user.setUserId(userJson.get("user_id").getAsInt());
this.user.serUserEmail(userJson.get("user_email").getAsString());
//so on.
}
At first let me say that the JSON you need to process here is broken by design so you should urge the guy / department / company to fix it.
Secondly, JSON processors like Jackson allow to parse polymorphic data structures like this easily, but they require some kind of type flag to distinguish one of another type (i.e. type: "user" and type: "lecturer"). There is also a way to do this without such type flags, but there is a lot more hand work involved. The last example here shows how to do it.
Yes, it is one possible solution. Gson ignores all fields, which names doesnt match #SerializedName annotation. So, you may try another solution without creating any more pojo classes- return result as String, and try to parse this string as both classes. If one result is empty- then you have another. But, if both kbjects isnt empty- then original response contain fields from both pojos

Cannot get JSON data from Jersey GET response

I was developing a restful web client and trying to get the JSON payload from the response of a GET method. I am using Jersey. But I just cannot read the JSON data using response.getEntity() method. I tried many methods including response.bufferEntity(), but the output always kept empty. Below is my code and output, and in addition I can see the JSON data right in the response packet captured in wireshark. I would really appreciate everyone trying to help figure out why or provide solution. Thank you!
Code:
public JSONObject Get(String requestPath){
ClientResponse response = webResource.path(requestPath)
.header("Content-Type", contTypeHeader )
.header("Accept",acceptHeader)
.header("Authorization", authZ )
.get(ClientResponse.class);
response.bufferEntity();
if (!(response.getStatus() == 201 || response.getStatus() == 200)) {
throw new RuntimeException("Failed : HTTP error code : " + response.getStatus());
}
System.out.println(response.getEntity(JSONObject.class));
return null;
}
and the output is always like this: {}
You can't use JSONObject unless you have a MessageBodyReader for it. See more at JAX-RS Entity Providers. The provider you are currently using (probably Jackson) only supports JavaBean POJOs or collections of them. For example if you have this JSON
{ "property1" : "value1", "property2": "value2" }
Then you would need to have a POJO like
public class Bean {
private String property1;
private String property2;
// getters and setters
}
Then you can do getEntity(Bean.class). The reason you are getting an empty object, is that the deserialization works off the setters. It looks for properties on the JSON, that matches a setters, and uses it the set the property. The JSON object has no setters for your JSON properties.

json and Spring MVC

I have trouble with Spring MVC and json.
I use SimplecartJS to generate json data like this :
{"currency":"RUR",
"shipping":250,
"tax":0,
"taxRate":0,
"itemCount":2,
"item_name_1":"Name of product #1",
"item_quantity_1":6,
"item_price_1":159,
"item_options_1":"",
"item_name_2":"Name of product #2",
"item_quantity_2":2,
"item_price_2":159,
"item_options_2":"",
"form":{
"Fname":"UserName",
"Phone":"7123456789",
"Address":"UserAddress",
"Comment":"Comment Text"
}
}
My controller Spring
#RequestMapping(value = "/checkorder2", method = RequestMethod.POST, headers = "Accept=application/json")
public String test (#RequestBody OrderCon orderC)throws Exception{
ObjectMapper om = new ObjectMapper();
om.canSerialize(OrderCon.class);
System.out.println(om);
return test(orderC);
}
Code from client side
var url = 'http://localhost:8080/url'
jQuery.ajax({
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
contentType:'application/json',
dataType: "json",
type: "POST",
url: url,
data: JSON.stringify(data),
And my question:
When I send data to controller, I have a mistake 400 Bad request. The request sent by the client was syntactically incorrect. When adding a new item field name will "item_name_2","item_name_3" etc. How I can parse this. I try parse to List, Set but it is not working. Please help.
UPD1: OrderCon.java
public class OrderCon {
private List<String> form;
private List<List<String>> json;
getters and setters...
}
There is no way you are going to be able to parse that data to a class like you have, since you need to add dynamic properties to a Java class.
You do however have two options to get the data out
First option is parse the json to a HasMap of Strings, ie. change your controller signature to
public String test (#RequestBody Map<String, String> orderC)
The other option is to use a JsonNode and deal with the data as a tree, here is an example http://wiki.fasterxml.com/JacksonTreeModel
I personally would try the latter first
You're getting 400 Bad Request because Spring is trying to map the passed JSON directly to your OrderCon class.
For this to work, the class would need to map to the keys specified in the JSON.
IE:
JSON:
{
"name" : "foo",
"phone": "111-111-1111"
}
would map to:
public class someJsonPojo(){
String name;
String phone;
//setters & getters
}
I don't think this will work well since instead of getting an array of items, you only get an item appended to the list as a new key:value. You should be able to modify the JSON so that you can map to an array of Item objects, which contain the name, quantity, price, etc.

Pass parameters in the client

I use a RESTful Web Service. In this web service I must pass a bean that I want to save as a parameter.
Here is the server code:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Unidade inserir(Unidade unidade){
Session s = ConnectDb.getSession();
try {
s.getTransaction().begin();
s.save(unidade);
s.getTransaction().commit();
return unidade;
} catch (Exception e) {
e.printStackTrace();
s.getTransaction().rollback();
return null;
} finally {
s.close();
}
}
I have the following code in the client:
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource webResource = client.resource("http://localhost:8080/RestauranteWeb/rest/unidades/7");
Builder builder = webResource.accept(MediaType.APPLICATION_JSON);
GenericType<Unidade> genericType = new GenericType<Unidade>() {};
Unidade u = new Unidade();
u.setUnidSigla("KG");
//How to pass this bean as parameter?
Unidade response = builder.post(genericType);
System.out.println(response);
How can I pass the bean to the method as a parameter?
Using Jackson as a Serializer/DeSerializer
If your Unidade object is annotated with Jackson and/or a Deserializer is registered then you should be able to POST with a BODY that contains the JSON representing the Unidade object. It should be magically deserialized and rebuilt as an object on the server side.
Important
Make sure that you add a Content-Type header in the POST request with a value of application/json. Without this header your Jersey might not know what to do with the body.
You would use the Jackson ObjectMapper to serialize your Unidade object to JSON and send that instead of whatever that GenericType stuff is.
I have both Jersey and RESTEasy implementations that work seamlessly with Jackson in this manner.
How can I pass the bean to the method as a parameter?
Checkout the documentation for the post method:
/**
* Invoke the POST method with a request entity that returns a response.
*
* #param <T> the type of the response.
* #param c the type of the returned response.
* #param requestEntity the request entity.
* #return an instance of type <code>c</code>.
* #throws UniformInterfaceException if the status of the HTTP response is
* greater than or equal to 300 and <code>c</code> is not the type
* {#link ClientResponse}.
* #throws ClientHandlerException if the client handler fails to process
* the request or response.
*/
<T> T post(Class<T> c, Object requestEntity)
throws UniformInterfaceException, ClientHandlerException;
The method takes two parameters. First parameter is the expected response type, and second one is the entity which is going to be put in the request body.
What happens here, when performing the request Jersey would serialize the object passed as a request entity into the JSON string (hence you've set the header - accept(MediaType.APPLICATION_JSON)). When the response from the server arrives, Jersey will deserialize it (the inverted process as in case of requestEntity) and return you the object.
And what if my method receives more than 1 parameter? Because the post
method only acepts 1
Well you cannot do it with JAX-RS, and it makes little sense actually. You can pass multiple parameters to the method as #PathParam or a #MatrixParam, but there can be only one associated with the body (well you have only one body in our request, right?). Checkout answer to this question and checkout how to use #PathParam or #MatrixParam
Let's suppose instead of returning a "Unidade" class, my method
returns a String. So, it will receive a "Unidade" as parameter and
return a "String". How can i retrieve it in this case, passing the
"Unidade" instance as before?
I believe you could achieve that with post(String.class, unidadeInstance). The first parameter doesn't have to be the same as the second. It's valid to accept one parameter and return another. It is even valid to take a parameter and return nothing in the body (like you have done it in the code attached to your question). You could accept the body and send back response containing status 201 Created and Location header entry pointing to the URL of the newly created resource.
Not sure what's your purpose for GenericType. Anyway, try the code below.
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
Unidade u = new Unidade();
u.setUnidSigla("KG");
WebResource webResource = client.resource("http://localhost:8080/RestauranteWeb/rest/unidades/7");
Unidade response = webResource.accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON)
.post(Unidade.class, u);
I'm not sure if it helps but i had a similar problem.
My scenario was i need a webservice which had to receiver a bunch of values which are organized as a kind of profile. But this service has to handle that there are comming more profiles where still old clients using the service. The interface had to be as static as possible.
Our solution is very simple. We only post a single text field as content of the post. But this includes the serialized state of an profile object in JSON.
Pseudo code:
public class Profile1 {
...
public String asJSON() {
JSONObject obj = new JSONObject();
obj.put("profileAtr1", profileAtr1);
...
return obj.toString()
}
}
formParams.put("profile", profile.asJSON());
client.post(formParams);
This way it's not automaticly deserialized but it's easy to do it by hand.
We do this with a generic Profile object which can be created out of JSON String in the constructor.
Pseudo code:
public GenericProfile {
public GenericProfile(String json) {
JSONObject obj = new JSONObject(json);
String profileName = obj.getString("profileName");
if (profileName.equals("Profile1") {
this = new Profile1(obj); // I know this is not working ;) My impl. is a litle bit more complecated as that. I think i use a static method in the generic profile to create an instance i need.
} ...
}
}
And then in your webservice only have this one form param to handle and to deserialize ;)
Pseudo code:
public ResponseEnvelope coolServiceFunction(#FormParam("profile") String profileData) {
GenericProfile profile = new GenericProfile(profileData);
if (profile instanceof Profile1) {
do what you want
}
}
Sorry for the pseudo code, but i had alleady shutdown my dev vm and have no access to any repository any more :(
I think the biggest benefits from this solution is:
1. It can transport anything you can pack in JSON. I transfer BASE64 encoded binary blocks and heavyly encrypted textdata this way.
2. The easiest tutorial example of your REST Framework of the POST Service will provide all you need to do this.
3. You can be sure that your interface will stay for a long period of time.
Hope that helps

Categories