Mapping complex JSON objects in RestTemplate - java

The structure of JSON looks like:
{
"id": "0001",
"type": "portable",
"name": "mobile",
"results":[
{
"company": "Apple",
"country": "US",
"city": "Cupertino"
},
{
"company": "Google",
"country": "Japan",
"city": "Tokyo"
}
]
}
I tried to map the above json to my "Response" class but I could only retrieve id, type and name. For json object "results" is shows results:[].
Here is the code I have implemented;
CompanyDetail class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class CompanyDetail {
public String company;
public String country;
public String city;
//getters and setters
}
Company class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class CompanyD {
public String name;
public List<Detail> result;
//getters and setters
}
Implementation class:
#Override
public ResponseEntity<String> getResponseEntity() {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate .getForEntity(
"url", String.class);
logger.info("Response:"+response);
return response;
}
#Override
public Company getResponseObject() {
RestTemplate restTemplate=new RestTemplate();
Company comp=(Company) restTemplate.getForObject("url",
Company.class,200);
List<CompanyDetail> companyInfo = comp.getCompanyDetail();
for (CompanyDetail companyDetail : companyInfo) {
logger.info("Country:"+companyDetail.getCountry());
logger.info("City:"+companyDetail.getCity());
logger.info("Company:"+companyDetail.getCompany());
}
return comp;
From both the implementation method I am getting null values for the "results" json object. I could not figure out what is wrong with my code.
Thanks in advance.

I'm having the same issue however in your case it looks like you misspelled "resluts".

Related

Not able to deserialize nested JSON array Spring Resttemplate

I am not able to deserialize nested JSON array from response JSON using Spring Rest template.
JSON response which I am consuming is as follows
[
{
"creationTime": "2023-01-13",
"code": "456",
"cid": "123",
"priority": "CRITICAL",
"reviewDate": null,
"systemCall": [
{
"creationTime": "2023-01-13",
"status": null,
"id": "787878",
"modificationTime": "2023-01-13",
"creatorId": "ABC"
},
{
"creationTime": "2023-01-14",
"status": null,
"id": "787879",
"modificationTime": "2023-01-14",
"creatorId": "DEF"
}
],
level: "1"
}
]
And My model classes as follows
public class Resolution {
private String creationTime;
private String code;
private String cid;
private String priority;
private String reviewDate
private List<SystemCallVo> systemCall;
private String level;
public Resolution(){
}
//Getters and Settrs
}
public class SystemCallVo {
private String creationTime;
private String status;
private String id;
private String modificationTime;
private String creatorId;
public SystemCallVo(){
}
//Getters and Setters
}
public class ResolutionVo extends Resolution{
public ResolutionVo(){
}
}
I am calling my endpoint using rest template as follows.
ResponseEntity<List<ResolutionVo>> response = this.restTemplateConfig.restTemplate().exchange(builder.toUriString(), HttpMethod.POST, httpEntity, new ParameterizedTypeReference<List<ResolutionVo>>() {
}, new Object[0]);
Problem is List systemCall object is always null in response received through resttemplate even though systemCall attribute is present in JSON whenever I hit endpoint through swagger.
There is a defect in RestTemplate.exchange that prevents the deserialisation of even moderately complex JSON objects.
Read the response as a String and then deserialise to List<ResolutionVo> with an com.fasterxml.jackson.databind.ObjectMapper instance as follows:
ResponseEntity<String> response = this.restTemplateConfig.restTemplate().exchange(builder.toUriString(), HttpMethod.POST, httpEntity, String.class, new Object[0]);
String body = response.getBody();
List<ResolutionVo> value = objectMapper.readValue(body, new TypeReference<List<ResolutionVo>>() {});
I think this is a related issue.

How to add a list to an object in Spring

I have this object class that has a list of customers as an attribute:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class PeopleDTO {
private String processType;
private String operation;
private String entity;
private String entityType;
private Long id;
private Document document;
#Getter
#Setter
class Customer {
private String systemId;
private String customerId;
}
private List<Customer> customers;
}
This list is retrieved calling another microservice using webclient as follows:
public Mono<CuCoPerson> getCuCoPerson(Integer cucoId, String GS_AUTH_TOKEN) {
WebClient webClient = WebClient.create();
return webClient.get()
.uri(GET_RELATION_BY_ID + cucoId)
.header("Accept", "application/json")
.header("Authorization", GS_AUTH_TOKEN)
.retrieve()
.bodyToMono(CuCoPerson.class)
.map(cuCoPerson -> {
List<CustomerRelation> matches = cuCoPerson.getRelatedCustomers()
.stream()
.filter(relation -> relation.getSystemId().equals(400) || relation.getSystemId().equals(300) || relation.getSystemId().equals(410))
.filter(relation -> relation.getCustomerId().contains("F"))
.collect(Collectors.toList());
cuCoPerson.setRelatedCustomers(matches);
return cuCoPerson;
});
}
This method return a cucoPerson as follows:
{
"id": 1,
"relatedCustomers": [
{
"customerId": "xxx",
"systemId": 999
}
]
}
So now I want to add this object to my PeopleDTO class, but I don't know how. This is what I've done son far (hardcoded):
public PeopleDTO createPeople(Long id) {
PeopleDTO people = new PeopleDTO();
people.setProcessType("ONLINE");
people.setOperation("UPDATE");
people.setEntity("DOCUMENT");
people.setEntityType("DOCUMENT");
people.setIdCuco(id);
people.setDocument(new Document());
people.setCustomers(......);
}
So as you can see I don't know how to add a Mono in the last line.
The expected result should be like this:
{
"type": "ONLINE",
"operation": "UPDATE",
"id": 1,
"entity": "DOCUMENT",
"entityType": "NIE",
"documents": {
"id": 1,
"additionals": {
"issuing_authority": "Spain",
"country_doc": "ES",
"place_of_birth": "",
"valid_from": "1995-08-09",
"valid_to": "0001-01-01"
},
"code": "X12345",
"typeDocument": "NIE"
},
"id": 1,
"relatedCustomers": [
{
"customerId": "xxx",
"systemId": 999
}
]
}
first, create a list of customers like:
List<Customer> customers=new ArrayList<>;
Then add all the Customers to it one by one using a loop,
then you can directly add that to your object like
people.setCustomers(customers);
your object assignment should look something like:
public PeopleDTO createPeople(Long id) {
PeopleDTO people = new PeopleDTO();
people.setProcessType("ONLINE");
people.setOperation("UPDATE");
people.setEntity("DOCUMENT");
people.setEntityType("DOCUMENT");
people.setIdCuco(id);
people.setDocument(new Document());
List<Customer> customers=new ArrayList<>;
//add data to customer
people.setCustomers(customers);
}

Java mapping JSON with POJO using jackson

I want to create this JSON using jakson annotated POJOS. The issue I have when I create a new class without #JsonProperty annotation to represent the last {"id":"123ccc","role":"dddd"}, it by default take the class name and create something like "customer":{"id": "123ccc","role":"dddd"}.
The JSON Structure I indent to build
{
"relatedParty": [
{
"contact": [
{
"mediumType": "xxx",
"characteristic": {
"city": "xxx",
"country": "xxx"
}
},
{
"mediumType": "yyy",
"characteristic": {
"emailAddress": "yyy#yy.yyy"
}
}
],
"role": "ccc",
"fullName": "ccc"
},
{
"id": "123ccc",
"role": "dddd"
}
]
}
The JSON I'm receiving from the below code.
{
"relatedParty": [
{
"contact": [
{
"mediumType": "xxx",
"characteristic": {
"city": "xxx",
"country": "xxx"
}
},
{
"mediumType": "yyy",
"characteristic": {
"emailAddress": "yyy#yy.yyy"
}
}
],
"role": "ccc",
"fullName": "ccc"
},
"customer" : {
"id": "123ccc",
"role": "dddd"
}
]
}
What would be a workaround to get the exact JSON format as the image. Current Implementation is below.
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class RelatedParty {
#JsonProperty(value = "contact")
private List<Contact> contact;
#JsonProperty(value = "role")
private String role;
#JsonProperty(value = "fullName")
private String fullName;
private Customer customer;
public List<Contact> getContact() {
return contact;
}
public void setContact(List<Contact> contact) {
this.contact = contact;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
public class Customer {
#JsonProperty(value = "id")
private String id;
#JsonProperty(value = "role")
private String role;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
You need to create additional and different POJO classes to model your JSON correctly. Basically, JSON arrays will be handle in Java lists, and JSON objects will be handled in Java classes.
Starting from the inside (most nested level) of the JSON, and working our way out:
NOTE: getters and setters not shown here
Characteristic.java
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Characteristic {
#JsonProperty("city")
private String city;
#JsonProperty("country")
private String country;
#JsonProperty("emailAddress")
private String emailAddress;
}
Contact.java (contains our characteristics):
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Contact {
#JsonProperty("mediumType")
private String mediumType;
#JsonProperty("characteristic")
private Characteristic characteristic;
}
The above two classes handle the innermost objects. If we remove them from your target JSON, that leaves the following:
{
"relatedParty": [{
"contact": [...],
"role": "ccc",
"fullName": "ccc"
}, {
"role": "dddd",
"id": "123ccc"
}]
}
Note that the contact field is a JSON array, not an object - so we do not create a Java Contact class (which would be for a JSON object).
To handle the above I create two more classes:
RelatedPartyInner.java (contains a list of contacts)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class RelatedParty_ {
#JsonProperty("contact")
private List<Contact> contact = null;
#JsonProperty("role")
private String role;
#JsonProperty("fullName")
private String fullName;
#JsonProperty("id")
private String id;
}
RelatedParty.java (wraps everything in an outer object):
#JsonInclude(JsonInclude.Include.NON_NULL)
public class RelatedParty {
#JsonProperty("relatedParty")
private List<RelatedPartyInner> relatedParty = null;
}
To test this I create the following data:
Characteristic chr1 = new Characteristic();
chr1.setCity("xxx");
chr1.setCountry("xxx");
Characteristic chr2 = new Characteristic();
chr2.setEmailAddress("yyy#yy.yyy");
Contact con1 = new Contact();
con1.setMediumType("xxx");
con1.setCharacteristic(chr1);
Contact con2 = new Contact();
con2.setMediumType("yyy");
con2.setCharacteristic(chr2);
List<Contact> cons = new ArrayList<>();
cons.add(con1);
cons.add(con2);
RelatedPartyInner rpi1 = new RelatedPartyInner();
rpi1.setContact(cons);
rpi1.setRole("ccc");
rpi1.setFullName("ccc");
RelatedPartyInner rpi2 = new RelatedPartyInner();
rpi2.setId("123ccc");
rpi2.setRole("dddd");
List<RelatedPartyInner> rpis = new ArrayList<>();
rpis.add(rpi1);
rpis.add(rpi2);
RelatedParty rp = new RelatedParty();
rp.setRelatedParty(rpis);
Finally, we can generate the JSON:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(new File("rp.json"), rp);
The resulting file contains the following:
{
"relatedParty": [{
"contact": [{
"mediumType": "xxx",
"characteristic": {
"city": "xxx",
"country": "xxx"
}
}, {
"mediumType": "yyy",
"characteristic": {
"emailAddress": "yyy#yy.yyy"
}
}],
"role": "ccc",
"fullName": "ccc"
}, {
"role": "dddd",
"id": "123ccc"
}]
}

Could not extract response in RestTemplate

I have a SpringBoot app. with this config file:
#Configuration
public class ApplicationConfig {
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM));
restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
return restTemplate;
}
}
and this class:
#Builder
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(NON_NULL)
public class GeolocationAddress {
private Integer placeId;
private String licence;
private String osmType;
private Integer osmId;
private List<String> boundingbox = null;
private String lat;
private String lon;
private String displayName;
private String _class;
private String type;
private Double importance;
private Address address;
}
and this service:
public GeolocationAddress searchFromAddress(String address) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>(headers);
return restTemplate.exchange("http://nominatim.openstreetmap.org/search?" + address, HttpMethod.GET, entity, GeolocationAddress.class).getBody();
}
but I have this error when running the service:
org.springframework.web.client.UnknownContentTypeException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.bonansa.domain.GeolocationAddress] and content type [text/html]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:126)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:998)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:981)
It appears you’re missing format query param based on api doc.
In your case i think it should be format=json
https://nominatim.org/release-docs/develop/api/Search/
Example copies from doc - JSON with address details
https://nominatim.openstreetmap.org/?addressdetails=1&q=bakery+in+berlin+wedding&format=json&limit=1
{
"address": {
"bakery": "B\u00e4cker Kamps",
"city_district": "Mitte",
"continent": "European Union",
"country": "Deutschland",
"country_code": "de",
"footway": "Bahnsteig U6",
"neighbourhood": "Sprengelkiez",
"postcode": "13353",
"state": "Berlin",
"suburb": "Wedding"
},
"boundingbox": [
"52.5460929870605",
"52.5460968017578",
"13.3591794967651",
"13.3591804504395"
],
"class": "shop",
"display_name": "B\u00e4cker Kamps, Bahnsteig U6, Sprengelkiez, Wedding, Mitte, Berlin, 13353, Deutschland, European Union",
"icon": "https://nominatim.openstreetmap.org/images/mapicons/shopping_bakery.p.20.png",
"importance": 0.201,
"lat": "52.5460941",
"licence": "Data \u00a9 OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright",
"lon": "13.35918",
"osm_id": "317179427",
"osm_type": "node",
"place_id": "1453068",
"type": "bakery"
}

Spring RestTemplate returns null object when trying to deserialize nested list of object

So this is the Json I am trying to convert to a Java bean
I am using jackson to bind JSON to my data objects
{
"legend": {
"id": "379ec5d8c",
"name": "Stabil Koos",
"payers": [{
"id": "ab1651df-b835-495558-a2a5-2e6d42f7a41e",
"ranking": 1,
"buyer": {
"id": "67a6359838-0fda-43a6-9e2b-51a7d399b6a1",
"nationality": "en",
"stats": {
"gameeCount": 16581,
"count": 99098
}
}
},
{
"id": "379ecw555d8c",
"ranking": 2,
"buyer": {
"id": "2b451d0eda-ab0c-4345660-be3f-6ba3bebf1f81",
"nationality": "no",
"stats": {
"gamerCount": 1182,
"count": 7113
}
}
}
]
}
}
My beans look like this ;
public class League implements Serializable {
private String id;
private String name;
#JsonUnwrapped
private List<Payer> payers;
// getters and setters
Payers bean :
public class Payers implements Serializable {
private String id;
private long ranking;
private Buyer buyer;
// getters and setters
I am using Rest Template and postForObject in Junit
#Before
public void beforeTest() {
restTemplate = new RestTemplate();
headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
entity = new HttpEntity(REQUEST, headers);
}
And my final code to retrieve the object is :
#Test
public void retrieveData() {
League league = restTemplate.postForObject(ENDPOINT_URL, entity, League.class);
System.out.println(legend);
}
The JSON you show is for an object that has a league property which is a League object and not a league object itself. You need an additional response class:
class LeagueResponse {
private League league;
League getLeague() { return league; }
}
and:
LeagueResponse leagueResponse = restTemplate.postForObject(ENDPOINT_URL, entity, LeagueResponse.class);
League league = leagueResponse.getLeague();

Categories

Resources