Unable to make restTemplate call with Generics for nested Json - java

I am trying to make a restTemplate call for API testing. The json returned is a nested one with multiple levels.
{
"code": 200,
"data": {
"result": {
"publicId": "xyz"
}
}
}
I have the following classes acting as wrapper :
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public abstract class RestCallResponse<T> {
private int code;
protected RestCallResponse(int code) {
this.code = code;
}
protected RestCallResponse(){}
#JsonProperty("data")
public Map<?, ?> getRestCallResponse() {
return ImmutableMap.of("result", getResult());
}
#JsonIgnore
protected abstract T getResult();
public int getCode() {
return code;
}
}
And then a SuccessRestResponse class extending this abstract class :
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE)
public class SuccessRestResponse<T> extends RestCallResponse<T> {
#JsonProperty("result")
private T result;
public SuccessRestResponse() {
}
public SuccessRestResponse(T result) {
super(HttpStatus.SC_OK);
this.result = result;
}
protected T getResult() {
return this.result;
}
}
Then finally I have the actual data POJO :
public final class CreatedResponse {
#JsonProperty
private final EntityId publicId;
public CreateCreativeResponse(EntityId publicId) {
this.publicId = publicId;
}
}
In the test case, I am making a call as such :
ResponseEntity<SuccessRestResponse<CreatedResponse>> newResponse =
restTemplate.exchange(requestEntity, new ParameterizedTypeReference<SuccessRestResponse<CreatedResponse>>() {});
But I am getting the following error :
nested exception is org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: null value in entry: result=null (through reference chain: com.inmobi.helix.test.api.dao.SuccessRestResponse["data"]);
Any suggestions? Where am I going wrong?

I solved the problem with a workaround. Still don't know what's wrong with the above piece of code though.
I got rid of the class RestCallResponse<T> and edited the field members in SuccessRestResponse<T> to look like this :
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class SuccessRestResponse<T> {
private int code;
private Map<String, T> data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Map<String, T> getData() {
return data;
}
#JsonIgnore
public T getResult() {
return data.get("result");
}
public void setData(Map<String, T> data) {
this.data = data;
}
}
This corresponds to the nested json while deserialization.
P.S. - Would still like to know what went wrong in my above code
though. As in, why did class hierarchy not work for me.

Related

How to map response with objects instead of a list of objects with Spring Boot in Api Client

How should I create a class for an api response that returns objects instead of a list of objects? I'm using Spring Boot and RestTemplate and don't know how to go about it.
This is a response:
{
"status":200,
"data":{
"1":{
"id":"1",
"auth":"manual",
"confirmed":"1",
"policyagreed":"0",
"deleted":"0",
"suspended":"0",
"mnethostid":"1",
"username":"guest",
"password":"",
"idnumber":"",
"firstname":"Guest user",
"lastname":" ",
"email":"root#localhost",
"emailstop":"0",
"icq":"",
"skype":"",
"yahoo":"",
"aim":"",
"msn":"",
"phone1":"",
"phone2":"",
"institution":"",
"department":"",
"address":"",
"city":"",
"country":"",
"lang":"en",
"calendartype":"gregorian",
"theme":"",
"timezone":"99",
"firstaccess":"0",
"lastaccess":"0",
"lastlogin":"0",
"currentlogin":"0",
"lastip":"",
"secret":"",
"picture":"0",
"url":"",
"description":"",
"descriptionformat":"1",
"mailformat":"1",
"maildigest":"0",
"maildisplay":"2",
"autosubscribe":"1",
"trackforums":"0",
"timecreated":"0",
"timemodified":"1584114527",
"trustbitmask":"0",
"imagealt":null,
"lastnamephonetic":null,
"firstnamephonetic":null,
"middlename":null,
"alternatename":null,
"moodlenetprofile":null
},
"2":"...."
}
}
I tried something like this:
public class MoodleResponse {
private Integer status;
private Data data;
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
public class Data {
private HashMap<String, MoodleUser> map;
public HashMap<String, MoodleUser> getMap() {
return map;
}
public void setMap(HashMap<String, MoodleUser> map) {
this.map = map;
}
}
It doesn't work. I've never encountered anything like this.
you will need to create a model class here which has all attributes present in response and each attribute must be mapped with #JsonProperty
key here is to understand #JsonPropery annotation
for eg
public class Data{
#JsonProperty("id")
private String id;
#JsonProperty("auth")
private String auth;
}
and soo on

Jersey not working for POST, PUT

I have a problem which seems to have a simple solution but I haven't been able to solve for months:
I'm trying to POST/PUT JSONs, and having the server recognize and deserialize them as their corresponding classes. But the server is not able to parse the entity, and returns a 500 error code.
This has to do with the fact that the generated code expects an interface (Apple) instead of a 'normal' Java class (AppleImpl).
I know this because the methods work if I change the interface (Apple) with the implementation (AppleImpl) in the endpoint, since the server detects the type and deserializes it correctly, but it is tedious and error-prone to manually change the interfaces to implementations every time that a change is made in the RAML.
In the Java backend, I'm using an entity, interface and endpoint, all of which were generated using raml-to-jax-rs v3.0.2. I'm using Maven with Jersey (jersey-bom) v2.27.
To summarize, I would like to know how to successfully deserialize an interface in Jersey/JAX-RS. Am I missing an annotation, are my Maven package versions wrong, or is it something else?
I would very much appreciate if someone could help me with this, and I will provide more information if necessary. Below is some sample generated code so that the problem is clearer.
Sample generated code:
(1/3) Sample generated interface Apple:
#JsonDeserialize(
as = AppleImpl.class
)
public interface Apple {
#JsonAnyGetter
Map<String, Object> getAdditionalProperties();
#JsonAnySetter
void setAdditionalProperties(String key, Object value);
#JsonProperty("name")
String getName();
#JsonProperty("name")
void setName(String name);
#JsonProperty("values")
List<String> getValues();
#JsonProperty("values")
void setValues(List<String> values);
#JsonProperty("defaultValue")
String getDefaultValue();
#JsonProperty("defaultValue")
void setDefaultValue(String defaultValue);
}
(2/3) Sample generated class AppleImpl:
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"name",
"values",
"defaultValue"
})
public class AppleImpl implements Apple {
#NotNull
#JsonProperty("name")
private String name;
#JsonProperty("values")
#NotNull
private List<String> values;
#JsonProperty("defaultValue")
#NotNull
private String defaultValue;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("name")
public String getName() {
return this.name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
#JsonProperty("values")
public List<String> getValues() {
return this.values;
}
#JsonProperty("values")
public void setValues(List<String> values) {
this.values = values;
}
#JsonProperty("defaultValue")
public String getDefaultValue() {
return this.defaultValue;
}
#JsonProperty("defaultValue")
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperties(String key, Object value) {
this.additionalProperties.put(key, value);
}
#Override
public boolean equals(Object o) {
if (o == null) return false;
if (this == o) return true;
if (getClass() != o.getClass()) return false;
AppleImpl other = (AppleImpl) o;
return java.util.Objects.equals(this.name, other.name) && java.util.Objects.equals(this.values, other.values) && java.util.Objects.equals(this.defaultValue, other.defaultValue) && java.util.Objects.equals(this.additionalProperties, other.additionalProperties);
}
#Override
public int hashCode() {
return Objects.hash(name,values,defaultValue,additionalProperties);
}
}
(3/3) Sample generated POST endpoint:
#Override
public PostApplesResponse postApples(String xSessionToken, Apple bean) {
try {
MUser user = AuthUtils.authenticates(xSessionToken);
ModelMapperApple.get().validateBean(bean);
MApple entity = ModelMapperApple.get().b2e(bean);
MApple entityOut = AppleService.getInstance().post(user, entity);
AppleResponse beanOut = ModelMapperApple.get().e2b(entityOut);
return PostApplesResponse.respond201WithApplicationJson(beanOut);
} catch (BadRequestException e) {
CRUDUtils.logException(e);
return PostApplesResponse.respond400WithApplicationJson(ErrorMessageUtils.build(e));
} catch (UnauthorizedException e) {
CRUDUtils.logException(e);
return PostApplesResponse.respond401();
} catch (ConflictException e) {
CRUDUtils.logException(e);
return PostApplesResponse.respond409();
} catch (Exception e) {
CRUDUtils.logException(e);
return PostApplesResponse.respond500();
}
}

Unable to parse json with List elements inside it

I have a Java class which has 2 List Object inside it and i am Json serializing the parent class.
#JsonSerialize
public class RequestSalesJson {
#JsonProperty("nonUniqueSalesList")
private List<SalesDataJson> getNonUniqueSalesDataJson;
#JsonProperty("uniqueSalesList")
private List<SalesDataJson> uniqueSalesDataJson;
public List<SalesDataJson> getGetNonUniqueSalesDataJson() {
return getNonUniqueSalesDataJson;
}
public void setGetNonUniqueSalesDataJson(List<SalesDataJson> getNonUniqueSalesDataJson) {
this.getNonUniqueSalesDataJson = getNonUniqueSalesDataJson;
}
public List<SalesDataJson> getUniqueSalesDataJson() {
return uniqueSalesDataJson;
}
public void setUniqueSalesDataJson(List<SalesDataJson> uniqueSalesDataJson) {
this.uniqueSalesDataJson = uniqueSalesDataJson;
}
}
SalesReturnJson.java
#JsonSerialize
public class SalesReturnJson {
#JsonProperty("starttime")
private String startTime;
#JsonProperty("pn")
private String partNumber;
#JsonProperty("so")
private String SalesOrderNumber;
#JsonProperty("wo")
private String workOrderNumber;
#JsonProperty("loc")
//other variables declared..
}
Controller.java :-
#RequestMapping(value = "/addAllSalesData",method = RequestMethod.POST)
public void addAllSalesData(#RequestBody RequestSalesJson requestSalesJsons){
log.info("POST : '/addSalesData'");
try{
System.out.print("In Controller "+requestSalesJsons.getUniqueSalesDataJson());
//salesService.processSalesData(requestSalesJsons);
}
catch(Exception e){
// return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
The value here is coming to be null.
Below is the json i am using :-
{ "uniqueSalesJson": [{"SO":4000955,"Part Number":"000","Locator":255638,"Lot Number":"P01-2059139","Reservation Quantity":2,"Status":"Released to warehouse","COE":"Fabrication","ORG":"P07","Start_Time":"2017-09-19 11:21:36"},{"SO":4000955,"Part Number":"000","Locator":255652,"Lot Number":"P01-2059140","Reservation Quantity":10,"Status":"Released to warehouse","COE":"Fabrication","ORG":"P07","Start_Time":"2017-09-19 11:21:36"}],"nonUniqueSalesJson":[{"SO":4000992,"Part Number":"1276M84G15","Locator":12345,"Lot Number":"P01-2344141","Reservation Quantity":6,"Status":"PACKED","COE":"Fabrication","ORG":"P07","Start_Time":"2017-09-19 11:21:36"},{"SO":4000992,"Part Number":"1276M84G15","Locator":12345,"Lot Number":"P01-2344141","Reservation Quantity":6,"Status":"PICKED","COE":"Fabrication","ORG":"P07","Start_Time":"2017-09-19 11:21:36"}]}
There are some issues in your code that let me doubt that your application compiles. First of all, rename the SalesReturnJson class to SalesDataJson.
Then check your #JsonProperty annotations. The value here must match exactly the property key in the Json String. Refactoring all this stuff will lead you to your root entity class:
#JsonSerialize
public class RequestSalesJson {
#JsonProperty("nonUniqueSalesJson")
private List<SalesDataJson> nonUniqueSalesDataJson;
#JsonProperty("uniqueSalesJson")
private List<SalesDataJson> uniqueSalesDataJson;
...
}
and your SalesDataJson class (missing a lot of attributes which the mapper ignores by configuration):
#JsonSerialize
public class SalesDataJson {
#JsonProperty("Start_Time")
private String startTime;
#JsonProperty("Part Number")
private String partNumber;
#JsonProperty("SO")
private String SalesOrderNumber;
}
This sample works as expected with the com.fasterxml.jackson.databind.ObjectMapper
Hope that helps!

Realm can't create Nested Objects from Json

I've been trying to create a nested RealmObject using a json but it only creates the first Object and not the nested ones. I would appreciate a help on this.
my Realm classes:
Content.java
public class Content extends RealmObject {
private String uuid;
RealmList<ContentDetailModel> ContentDetail;
public Content() {
super();
this.uuid = UUID.randomUUID().toString();
}
public String getUuid() {
return uuid;
}
public RealmList<ContentDetailModel> getContentDetails() {
return ContentDetail;
}
public void setContentDetails(RealmList<ContentDetailModel> contentDetails) {
this.ContentDetail = contentDetails;
}
}
ContentDetailModel.java:
public class ContentDetailModel extends RealmObject {
String FileName;
String ContentTypeID;
RealmList<ContentDetailMetadataModel> ContentDetailMetadata;
RealmResults<Content> content = null;
public String getFileName() {
return FileName;
}
public void setFileName(String fileName) {
FileName = fileName;
}
public String getContentTypeID() {
return ContentTypeID;
}
public void setContentTypeID(String contentTypeID) {
ContentTypeID = contentTypeID;
}
public RealmList<ContentDetailMetadataModel> getContentDetailMetadata() {
return ContentDetailMetadata;
}
public void setContentDetailMetadata(RealmList<ContentDetailMetadataModel> contentDetailMetadataz) {
this.ContentDetailMetadata = contentDetailMetadataz;
}
}
and the rest of nested classes are like these. my Json string is as follows:
"
{
"Content":{
"ContentDetail":[
{
"FileName":"test.mp3",
"ContentTypeID":3,
"ContentDetailMetadata":{
"Metadata":[
{
"ID":2,
"Value":"2017-08-02 09:40:30"
},
{
"ID":1,
"Value":"35.73876557934912,51.50785446166992"
}
]
}
},
{
"FileName":"2.jpg",
"ContentTypeID":2,
"ContentDetailMetadata":[
{
"Metadata":{
"ID":2,
"Value":"2017-08-02 09:40:30"
}
},
{
"Metadata":{
"ID":1,
"Value":"35.73876557934912,51.50785446166992"
}
}
]
}
]
}
}
"
and the code I use to do it is :
realm.createObjectFromJson(json)
{
"Content":{
"ContentDetail":[
{
"FileName":"test.mp3",
"ContentTypeID":3,
"ContentDetailMetadata":[{
"Metadata":[
{
"ID":2,
"Value":"2017-08-02 09:40:30"
},
{
"ID":1,
"Value":"35.73876557934912,51.50785446166992"
}
]
}]
},
Translates to:
public class Root extends RealmObject {
private Content Content;
}
public class Content extends RealmObject {
private RealmList<ContentDetail> ContentDetail;
#LinkingObjects("Content")
private final RealmResults<Root> roots = null;
}
public class ContentDetail extends RealmObject {
private String FileName;
private long ContentTypeID;
//private ContentDetailMetadata ContentDetailMetadata;
private RealmList<ContentDetailMetadata> ContentDetailMetadata;
#LinkingObjects("ContentDetail")
private final RealmResults<Content> contents = null;
}
public class ContentDetailMetadata extends RealmObject {
private RealmList<Metadata> Metadata;
#LinkingObjects("ContentDetailMetadata")
private final RealmResults<ContentDetail> contentDetails = null;
}
public class Metadata extends RealmObject {
private long ID;
private String Value;
#LinkingObjects("Metadata")
private final RealmResults<ContentDetailMetadata> contentDetailMetadatas = null;
}
If your schema doesn't look like that, then createOrUpdateFromJson() won't work.
Personally I would advise against using this schema though, it's pretty bad as a Realm schema. It's advisable to parse the JSON and then map it into a schema that makes more sense!
It looks like your JSON has put all fields for the Content object under the Context JSON object instead of directly under the top-level object where it should be.
If you do this, it should work:
realm.createObjectFromJson(Content.class, json.getJSONObject("Content"));

Getting JsonMappingException while sending data to view

I am trying to show DB data to my webpage.
I have made following code when GET request to the #RequestMapping(value = "/api/binder").
but when get request came to this method it will fetch data (I have print on console and display well) but it doesn't map to my Java Script Ajax call, it's showing me an error.
Following is my code for to fetch data :
#Autowired
IBinderViewRepository repository;
#RequestMapping(method= RequestMethod.GET)
public #ResponseBody
List<BinderResponse> getBinders(){
List<BinderView> binders = repository.getBinders();
List<BinderResponse> responses = new ArrayList<>();
ModelMapper mapper = Mapper.getInstance();
for(int i = 0; i < binders.size(); i++){
System.out.println("In Loop");
BinderResponse response = mapper.map(binders.get(i),BinderResponse.class);
System.out.println("Data :: " + response.getBinderName());
responses.add(response);
}
return responses;
}
but it shows me following error :
HTTP Status 500 - Could not write JSON: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.ngl.dto.outgoing.BinderResponse["valid"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.ngl.dto.outgoing.BinderResponse["valid"])
Here is ajax call from knockout js :
ajax.get('api/binder').done(function(response){ ... }
Here BinderView and BinderResponse have same fields :
private String binderName;
private String binderAddress1;
and getter setter as well in both.
and repository.genBinders() method bring data from DB.
Here is insert method and works fine for me :
#RequestMapping(method= RequestMethod.POST,consumes = "application/json")
public #ResponseBody
IWebApiResponse addBinder(#RequestBody AddBinderForm binder){
.....
}
Shall I have to put any json annotation on my BinderResponse class ?
I don't understand where am i wrong ?Anyone pleas guide me.
UPDATE :
public class BinderResponse extends WebApiResponseBase {
private String binderName;
private String binderAddress1;
public String getBinderName() {
return binderName;
}
public void setBinderName(String binderName) {
this.binderName = binderName;
}
public String getBinderAddress1() {
return binderAddress1;
}
public void setBinderAddress1(String binderAddress1) {
this.binderAddress1 = binderAddress1;
}
}
BinderView :
public class BinderView extends BaseView {
private String binderName;
private String binderAddress1;
public String getBinderName() {
return binderName;
}
public void setBinderName(String binderName) {
this.binderName = binderName;
}
public String getBinderAddress1() {
return binderAddress1;
}
public void setBinderAddress1(String binderAddress1) {
this.binderAddress1 = binderAddress1;
}
}
In console it prints data / BinderName :
In Loop
Data :: ada
In Loop
Data :: tya
New Update :
Here is BaseView :
#MappedSuperclass
public abstract class BaseView implements IEntity {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name="id")
private long id;
public long getId() {
return id;
}
public void setId(long id) {
if (this.id != 0 && this.id != id) {
throw new IllegalStateException(
"The ID must not be changed after it is set.");
}
this.id = id;
}
}
and In IEntity :
public interface IEntity extends Serializable {
long getId();
void setId(long id);
}
WebApiResponseBase :
public class WebApiResponseBase implements IWebApiResponse {
private String _uri;
#Override
public String getUri() {
return _uri == null ? "" : _uri;
}
#Override
public void setUri(String uri) {
_uri = uri;
}
}
Jackson, by default, serializes an object's whole inheritance hierarchy, ie. the parent class fields as well. In the case of
public class BinderResponse extends WebApiResponseBase {
it seems like
Could not write JSON: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.ngl.dto.outgoing.BinderResponse["valid"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.ngl.dto.outgoing.BinderResponse["valid"])
Jackson tries to serialize a field called valid from a getter called isValid (which is a conventional bean property name). The getter method, however, seems to throw a NullPointerException for whatever reason.
If you want Jackson to ignore it, you can annotate the getter with #JsonIgnore or your class with #JsonIgnoreProperties and specify the property name, ie. valid.
In my case when I used #JsonIgnore the exception has been gone but the problem was it couldn't receive that value from API Request anymore and Spring ignored it (obviously because of #JsonIgnore) So I investigated about the issue and figured out that the problem was the getter and setter.
I had the Integer property while my getter was int. So when I changed the getter to Integer my problem solved and error's gone.
private Integer purchaseId;
#JsonIgnore
public int getPurchaseId() {
return purchaseId;
}
public void setPurchaseId(int purchaseId) {
this.purchaseId = purchaseId;
}
Changed to :
private Integer purchaseId;
public Integer getPurchaseId() {
return purchaseId;
}
public void setPurchaseId(Integer purchaseId) {
this.purchaseId = purchaseId;
}
#Column(name="createddate")
private Date createdDate;
#Transient
private String formatedCreatedDate;
public String getFormatedCreatedDate() {
DateFormat dateFormat = new SimpleDateFormat("dd/mm/yyyy");
return dateFormat.format(this.getCreatedDate());
}
It throws the same exception because here may be null by calling getCreatedDate() value come so it can't format null date so keep null check here like:
Solution
public String getFormatedCreatedDate() {
DateFormat dateFormat = new SimpleDateFormat("dd/mm/yyyy");
Date createDdate=this.getCreatedDate();
if(createDdate!=null){
return dateFormat.format(createDdate);
}
return "-";
}

Categories