JSON Parsing with JAX-RS - java

I am building an REST API with JAX-RS. I have POST that consumes an JSON element:
The element is a class:
#XmlRootElement
public class EventData{
public long start;
public long end;
public Collection<Person> persons;
}
I have an method like this:
#POST
#Consumes({MediaType.APPLICATION_JSON})
public Response transactionRequest(EventData insert){
....}
if I post a JSON String of an EventData it works fine, but if I switch to:
#POST
#Consumes({MediaType.APPLICATION_JSON})
public Response transactionRequest(ArrayList<EventData> insert){
....}
and send an JSON String like this "{eventData:[{start:x,end:y,persons:[....]}]" it will build the ArrayList and its EventData objects, but the EventData object variables are null.
Can anybody help?

You need to send a JSON array consisting of JSON objects representing your EventData class.
The sample you've given isn't such a JSON array, but a JSON object with a single property named 'eventData' containing an JSON array.
Try something like this (based on your EventData class):
[
{ "start":1, "end":2, "persons":[] },
{ "start":3, "end":4, "persons":[] }
]
Notice that there is no mention of your EventData class, because JSON has no concept of named types -- it's just objects and arrays of objects; only the properties of objects have names.

Related

Issue with serializing JSON from a rest call

Newbie developer here. I am trying to make a call to a public API. The API receives the name of a drink as a string and returns information and recipe for that name. The response from the API looks like this:
{
"drinks":[
{
"id": ...
"name": ...
"recipe": ...
"category": ...
"alcoholic": ...
... many other fields ...
},
{
...
}
...
]
}
I am only interested in name, recipe and category. I have a domain class for this purpose that looks like this
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
public class Drink {
#JsonProperty("name")
private String name;
#JsonProperty("category")
private String category;
#JsonProperty("recipe")
private String recipe;
}
I also implemented a client to call the endpoint using restTemplate. Here is the call that client makes:
ResponseEntity<List<Drink>> response = restTemplate.exchange(
url,
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<Drink>>() {
});
My goal is to call the API, get the response and only the fields that I want and store it in a list of Drink. However when I try to run the app locally and make a call I am getting this error:
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.ArrayList<Drink>` from Object value (token `JsonToken.START_OBJECT`); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.ArrayList<Drink>` from Object value (token `JsonToken.START_OBJECT`)
When I use ResponseEntity<String> instead, it works but returns the whole json as a string, which does not seem like a good approach. How can I get this approach to work?
The problem is mismatch between json structure and object structure. The object you deserialize into must represent correctly the json. It's an object with a field drinks, which is an array of objects(drinks in your case). Correct java class would be:
public class Wrapper {
private List<Drink> drinks;
//getters and setters
#Override
public String toString() {
return "Wrapper{" +
"drinks=" + drinks +
'}';
}
}
Other option would be to write custom deserializer, which can extract drinks from the tree before deserializing directly into a list.]
Edit: Added toString() override for debugging purposes.

Creating POJOs for json with single field containing Array with RestTemplate

I am creating a client for the following format of JSON -
{
"results": [
{
"Product": "K265113",
"Language": "EN",
"LongText": "FIXTURE,INTERIOR,WALL"
}
]
}
The JSON always contains "results" field which is an array of a single element (it will always be a single element in this array). I just need LongText field from the JSON and nothing else. I am using Spring RESTTemplate.
I know that it works if I create two DTOs like -
public class ParentDTO
{
private List<ChildDTO> results;
public List<ChildDTO> getResults()
{
return results;
}
public void setResults(List<ChildDTO> results)
{
this.results = results;
}
}
public class ChildDTO
{
private String longText;
public String getLongText()
{
return longText;
}
#JsonProperty("LongText")
public void setLongText(String longText)
{
this.longText = longText;
}
}
But is there any way to read longText by creating a single DTO as the parent DTO is not having any useful field as I know there will always but just one element in the results array.
The reason you need only single DTO could be that you want only single class to perform this task. You can achieve that using ChildDTO as inner class which will make it more readable and maintainable.
The other way is to not parse the spring template response into DTOs instead use JSONNode class of Jackson databind API.
JsonNode root = objectMapper.readTree(response.getBody());
You can find more information at
https://fasterxml.github.io/jackson-databind/javadoc/2.8/com/fasterxml/jackson/databind/JsonNode.html
You can traverse down the tree and could retrieve the value of the attribute directly without any DTOs.

how to get request object without using #XmlRootElement for java objects in rest services

I am trying to generate rest service using Apache CXF and Jackson data-binding. here I don't want to use #XmlRootElement annotation. when I try below code the request object coming like a null object.
here is my service interface
#POST
#Path("/getusers/")
#Consumes("application/json")
#Produces("application/json")
public List<UserDetails> getusers(UserDetails userDetails) throws ServiceException;
here is my domain object
public class UserDetails implements Serializable{
private String userName;
private int userId;
public UserDetails(){
}
//getters and setters...
}
The Json Object looks like
{
"id" : "102",
"username" : "scott"
}
And I am getting null-pointer exception for the request object
how do I access my request object
Note: here I am using Jackson Data-Binding
Your JSON contains username while the member is called userName. Also, id and userId are different.
You have three options:
Change the names of the UserDetails members to match the fields in the JSON object.
Change the names of the JSON object to match the member names of UserDetails.
Use #JsonProperty to configure the JSON object name to be bound to a UserDetails member.

POJO attributes is returning as a Array for JSON in JAX-RS

I have a java web maven project with JAX-RS using resteasy version 2.2.1.GA implementation. All JAX-RS resources on the project produces y consumes application/json. My problem is that when I returning a single POJO, even an array of this, only serialize the values of the attributes.
Example:
Given the following classes:
public class Pojo {
private Integer attr1;
private String attr2;
// GETTERs and SETTERs
}
#Path("pojos")
#Consumes("application/json")
#Produces("application/json")
public class PojoResource {
#GET
public Response list() {
List<Pojo> listResult = new ArrayList<>();
Pojo pojo = new Pojo();
pojo.setAttr1(1);
pojo.setAttr2("asdf");
listResult.add(pojo);
return Response.ok().entity(listResult).build();
}
}
If I do a GET request to /pojos, the result for the example above is [[1, "asdf"]], instead of [{"attr1":1,"attr2":"asdf"}]
I don't know if a need to write a specific Provider. My project configuration is similar to this.
I realized my mistake is that data recovery in layer model is as a vector of objects and return that without processing (create a POJO that represent the data).

how to accept json array input in jersey Rest Webservice

Am developing a rest webservice using Jersey.Am slightly new to webservices. I need to pass List of customer as input to rest webservice. having issue in achieving it.
Below is my customer object class
#Component
public class customer {
private String customerId;
private String customerName;
And my endpoint is as below. addCust is the method that will be called on calling the webservice
#Path("/add")
#Produces({MediaType.APPLICATION_JSON})
#Consumes({MediaType.APPLICATION_JSON})
public String addCust(#Valid customer[] customers){
//And json input is as below
{customers:{"customerId":"1","customerName":"a"},
{"customerId":"2","customerName":"b"}}
But jersey is not able to convert json array to Array of Customers. It is returning 400. And the logs shows "no viable alternative at c". How to pass Json array as input to webservice and convert into Array or ArrayList. Any help appreciated.
Your json is invalid, field names should be always double quotted, and arrays are placed inside [] for example:
{"customers":[{"customerId":"1","customerName":"a"},
{"customerId":"2","customerName":"b"}]}
thats why jackson cannot unmarshall it. But this json will never fit your api.
Here is an example of what You should send:
[{"customerId":"1","customerName":"a"},{"customerId":"2","customerName":"b"}]
Another thing is that You can use collections instead of arrays:
#Path("/add")
#Produces({MediaType.APPLICATION_JSON})
#Consumes({MediaType.APPLICATION_JSON})
public String addCust(#Valid List<Customer> customers){
If you want to send a json like this:
{"customers":[{"customerId":"1","customerName":"a"},
{"customerId":"2","customerName":"b"}]}
then you have to wrap everything into class with "customers" property:
class AddCustomersRequest {
private List<Customer> customers;
public void setCustomers(List<Customer> customers) {
this.customers = customers;
}
public void getCustomers() {
return this.customers;
}
}
And use it in Your API:
#Path("/add")
#Produces({MediaType.APPLICATION_JSON})
#Consumes({MediaType.APPLICATION_JSON})
public String addCust(#Valid AddCustomersRequest customersReq){

Categories

Resources