I am calling an endpoint that returns back a JSON response.
Here is sample of the JSON response:
{
"main": {
"test":{
"Date": "2022-06-06",
"Id": 1234
},
"response" :[
{
"responseTime": 100,
"redirects": 0
}
]
}
}
Here is my code to get the JSON response:
HttpEntity<String> request = new HttpEntity<>(setHeaders());
ResponseEntity<Main> response = restTemplate.exchange(endpoint, HttpMethod.GET, request, Main.class);
I want to convert the response to an entity object, with the response section being a HashMap. I tried the following but I get an error that the conversion failed
public class Main {
private Test test;
private Response response;
}
public class Test{
private Date Date;
private int Id;
}
public class Response{
private Map<String, String> responseMap;
}
Can someone help me understand what I am doing wrong?
what you are getting back is an Object containing a main object
{
"main": {
"test":{
"Date": "2022-06-06",
"Id": 1234
},
"response" :[
{
"responseTime": 100,
"redirects": 0
}
]
}
}
which means
// Can be named whatever
public class Response {
private Main main;
//constructor, getter setters
}
public class Main {
private Test test;
private ArrayList<Data> response;
//constructor, getter setters
}
public class Test {
private LocalDate date;
private int id;
//constructor, getter setters
}
public class Data {
private int responseTime;
private int redirects;
//constructor, getter setters
}
And then you call and plase the response data in the top level object
ResponseEntity<Main> response = restTemplate.exchange(endpoint, HttpMethod.GET, request, Response.class);
Related
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.
I got a JSON response like this:
{
"status": "success",
"response": {
"entries": [
{
"id": 1,
"value": "test"
},
{
"id": 2,
"value": "test2"
}
]
}
}
And i want to map it with jackson-databind on an object like this:
public class Response {
#JsonProperty("status")
private String status;
#JsonProperty("response.entries")
private Collection<ResponseEntry> entries;
}
So i'm searching for an way to give #JsonProperty a path so it can skip the layer "response".
Welcome to Stack Overflow. You can define a wrapper class for your Collection<ResponseEntry> collection like below :
public class ResponseWrapper {
#JsonProperty("entries")
private Collection<ResponseEntry> entries;
}
The ResponseEntry class could be defined like below :
public class ResponseEntry {
#JsonProperty("id")
private int id;
#JsonProperty("value")
private String value;
}
Once defined these classes you can rewrite your old Response class like below :
public class Response {
#JsonProperty("status")
private String status;
#JsonProperty("response")
private ResponseWrapper responseWrapper;
}
You can flatten using the #JsonUnwrapped annotation.
You can have your classes like this
public class Response {
private String status;
private Collection<ResponseEntry> entries;
}
public class ResponseEntry {
#JsonUnwrapped
private Entry entry;
}
pubic class Entry{
private Integer id;
private String value;
}
I am struggling to deserialise complex nested Json data into Java objects I think my class structure is wrong. Here is my Json data:
{
"resultsPerPage": 20,
"startIndex": 0,
"totalResults": 2,
"result": {
"dataType": "CPE",
"feedVersion": "1.0",
"cpeCount": 2,
"feedTimestamp": "2021-03-19T13:06",
"cpes": [
{
"deprecated": false,
"cpe23Uri": "cpe:2.3:o:microsoft:windows_10:1511:*:*:*:*:*:x64:*",
"lastModifiedDate": "2015-12-09T17:28Z",
"titles": [
{
"title": "Microsoft Windows 10 1511 64-bit",
"lang": "en_US"
}
],
"refs": [
{
"ref": "https://www.microsoft.com/en-us/",
"type": "Vendor"
}
],
"deprecatedBy": [],
"vulnerabilities": [
"CVE-2016-0174",
"CVE-2016-0171"
]
}
Here is the class I map the Json data to:
public class RESPONSE {
Result result;
}
class Result {
List<Cpes> cpes;
}
class Cpes {
String cpe23Uri;
List<Titles> titles;
List<String> vulnerabilities;
}
class Titles{
String title;
}
When I debug my code r in the below code is null and I think it's because my RESPONSE class isn't set up right.
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
RESPONSE r = mapper.readValue(response.getContent(), RESPONSE.class);
System.out.println(r);
Your object model should match the structure of the JSON you are trying to read. For example, it'll have to look something like the following:
public class Response {
private int resultsPerPage;
private int startIndex;
private int totalResults;
private Result result;
// Should include getters and setters
}
public class Result {
private String dataType;
private String feedVersion;
private int cpeCount;
private String feedTimestamp;
private CPE[] cpes;
// Should include getters and setters
}
public class CPE {
private boolean deprecated;
private String cpe23Uri;
private String lastModifiedDate;
private Title[] titles;
private Ref[] refs;
private String[] deprecatedBy;
private String[] vulnerabilities;
// Should include getters and setters
}
public class Title {
private String title;
private String lang;
// Should include getters and setters
}
public class Ref {
private String ref;
private String type;
// Should include getters and setters
}
Note that to keep the code sample short, I've omitted the getters and setters.
Edit: As Tugrul pointed out below, since fail on unknown property is disabled, it won't fail if there are missing fields in your model. The only issue is the missing getters and setters.
I also found another way to solve this issue for future reference.
I used a tree data structure to access my Json fields which means I can just declare a flat class:
public class Test {
private String cpe23Uri;
private String title;
private List<String> vulnerabilities;
public String getCpe23Uri() {
return cpe23Uri;
}
public void setCpe23Uri(String cpe23Uri) {
this.cpe23Uri = cpe23Uri;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<String> getVulnerabilities() {
return vulnerabilities;
}
public void setVulnerabilities(List<String> vulnerabilities) {
this.vulnerabilities = vulnerabilities;
}
}
I then mapped using a Tree
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JsonNode resultNode = mapper.readTree(response.getContent());
Test t = new Test();
t.setCpe23Uri(resultNode.get("result").get("cpes").get(0).get("cpe23Uri").textValue());
I did a get request to a external RESTful api and receive as response a json object with this structure:
{
"data": {
"id": 1,
"name": "John Doe",
"email": "doe#john.com",
"urlPicture": "urlPicture.com/82819",
"address": {
"street": "My street",
"number": "29",
"city": "Nurnberg",
"country": "Germany"
}
}
}
I don't need all stuff of this response, I want only some fields to save in a database.
My POJO classes are similar to this pseudocode:
public class Data{
private User user;
// getters and setters
}
public class User{
private int id;
private String name;
private String urlPicture;
private String country;
// getters and setters
}
But, when I try to extract fields that I want, I receive null in this fields
public void testResponse(){
RestTemplate restTemplate = new RestTemplate();
Data data = new Data();
User user = new User();
Gson gson = new Gson();
String response = restTemplate.getForObject(
"https://apifrommyrequest.com/user/{id}",
String.class,
73442);
user = gson.fromJson(response, User.class);
System.out.println(user);
}
```
My output:
22:20:57.641 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET https://apifrommyrequest.com/user/73442
22:20:57.672 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
22:20:58.243 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
22:20:58.247 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json;charset=UTF-8"
Data(id=0, name=null, urlPicture=null, country=null)
I really don't know how to do anymore.
Try out the below code. You can't directly access the country, it is a value placed in a nested object. And also since response body returns as data you can't convert it to a User object. first, you need to convert it to a Data object.
public class User {
private int id;
private String name;
private String urlPicture;
private Address address;
// getters and setters
}
public class Address {
private String country;
// getters and setters
}
public void testResponse(){
RestTemplate restTemplate = new RestTemplate();
Data data = new Data();
User user = new User();
Gson gson = new Gson();
String response = restTemplate.getForObject(
"https://apifrommyrequest.com/user/{id}",
String.class,
73442);
data = gson.fromJson(response, Data.class);
System.out.println(data);
}
If you want, you can design your POJO like this too(to reduce nested POJOs):
public class Data {
private User data;
//getters and setters
}
public class User {
private int id;
private String name;
private String urlPicture;
private String country;
#JsonProperty("address")
private void unpackNested(Map<String,String> elements)
{
this.country = elements.get("country");
}
//getters and setters
}
Then finally deserialize on Data class
you dont really need to do the conversion from string to object, resttemplate already use jackson that do it for u , just have to
YourObject response = restTemplate.getForObject(
"https://apifrommyrequest.com/user/{id}",
YourObject.class,
73442);
then it is going to do the mapping to your pojo objects, u dont really need gson in this scenario.
I have this object which is converted into following format but it does not wrap it properly.
#JsonProperty("code")
private String code;
#JsonProperty("message")
private String msg;
#JsonProperty("assign")
private SomeVO someVO;
//getter, setters
to this format:
{
"status": {
"code": $value,
"message": $value
},
"data":{
"assign" {
"schemaLayoutFileName" : $value
"dataStoreTargetLocationText" : $value
}
}
}
How can it be done?
The class you have defined does not match the JSON you want to parse. Try the following design (if the class attributes names match the JSON properties names, you won't need #JsonProperty):
public class Foo {
private Status status;
private Data data;
// Getters and setters
}
public class Status {
private String code;
private String value;
// Getters and setters
}
public class Data {
private Assign assign;
// Getters and setters
}
public class Assign {
private String schemaLayoutFileName;
private String dataStoreTargetLocationText;
// Getters and setters
}