I'm trying to parse a json string like the one below but I'm getting an error when the rest endpoint is hit
Failed to read HTTP message:
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 2 path $
{
"resources": [
{
"resourceType": "Car",
"resourceId": "Car1"
},
{
"resourceType": "Truck",
"resourceId": "Truck1"
}
],
"topics": [
"some_data",
"some_event"
]
}
#PostMapping(value = "/subscribe", consumes = MediaType.APPLICATION_JSON_VALUE)
public void create(#RequestBody String subscriptionRequest)
throws Exception
{
SubscriptionRequest request = new Gson().fromJson(subscriptionRequest, SubscriptionRequest.class);
if ( request.getResources().isEmpty() || request.getTopics().isEmpty() )
{
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
other code ....
}
public class SubscriptionRequest
{
private List<Resources> resources = null;
private List<String> topics = null;
public SubscriptionRequest()
{
super();
}
public List<Resources> getResources()
{
return this.resources;
}
public void setResources(List<Resources> resources)
{
this.resources = resources;
}
public List<String> getTopics()
{
return this.topics;
}
public void setTopics(List<String> topics)
{
this.topics = topics;
}
}
public class Resources {
private List<Resource> resources = null;
private int resourceId;
public Resources()
{
super();
}
public List<Resource> getResources() {
return this.resources;
}
public void setResources(List<Resource> resources) {
this.resources = resources;
}
}
public class Resource {
private String resourceType;
private String resourceId;
public Resource()
{
super();
}
public Resource(String resourceType, String resourceId)
{
this.resourceType = resourceType;
this.resourceId = resourceId;
}
public String getResourceType()
{
return this.resourceType;
}
public void setResourceType(String resourceType)
{
this.resourceType = resourceType;
}
public String getResourceId()
{
return this.resourceId;
}
public void setResourceId(String resourceId)
{
this.resourceId = resourceId;
}
}
public class AppConfig extends WebMvcConfigurerAdapter
{
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
.allowedHeaders("X-Requested-With,Content-Type,Accept,Origin");
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
GsonHttpMessageConverter msgConverter = new GsonHttpMessageConverter();
Gson gson = new GsonBuilder().setPrettyPrinting().create();
msgConverter.setGson(gson);
converters.add(msgConverter);
}
}
Normally, you'd be injecting the deserialized type directly:
public void create(#RequestBody SubscriptionRequest subscriptionRequest)
and Spring takes care of deserializing it. Here, you expect a string, so Spring attempt to read the JSON input, which is an object, and deserialize it as a string, so it breaks (Expected a string but was BEGIN_OBJECT).
Try surrounding the entire input in double quotes:
"{
"resources": [
{
"resourceType": "Car",
"resourceId": "Car1"
},
{
"resourceType": "Truck",
"resourceId": "Truck1"
}
],
"topics": [
"some_data",
"some_event"
]
}"
This will likely resolve your immediate issue, but isn't what you want to do. Instead you should likely leave it to Spring as described above.
Related
I am calling an API using rest template like below:
ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, entity, String.class);
And here is the json response string that i receive from the API
{
"data": {
"individuals": [
{
"cust_xref_id": "abf",
"cust_frd_alrt_in": "n",
"cust_satis_trd_ct": "4",
"gam_open_rv_trd_ct": "4",
"cust_extnl_delinq_90_day_ct": "1",
"cust_extnl_delinq_in": "y"
}
]
}
}
how can i map this response into a pojo? please help.
Required classes for the conversion are below,
1. DataDTO
public class DataDTO {
private IndividualList data;
public IndividualList getData() {
return data;
}
public void setData(IndividualList data) {
this.data = data;
}}
2. IndividualList
public class IndividualList {
private List<IndividualDTO> individuals;
public List<IndividualDTO> getIndividuals() {
return individuals;
}
public void setIndividuals(List<IndividualDTO> individuals) {
this.individuals = individuals;
}}
3. IndividualDTO
public class IndividualDTO {
#JsonProperty("cust_xref_id")
private String custXrefId;
#JsonProperty("cust_frd_alrt_in")
private String custFrdAlrtIn;
#JsonProperty("cust_satis_trd_ct")
private String custSatisTrdCt;
#JsonProperty("gam_open_rv_trd_ct")
private String gamOpenRvTrdCt;
#JsonProperty("cust_extnl_delinq_90_day_ct")
private String custExtnlDelinq90DayCt;
#JsonProperty("cust_extnl_delinq_in")
private String custExtnlDelinqIn;
public String getCustXrefId() {
return custXrefId;
}
public void setCustXrefId(String custXrefId) {
this.custXrefId = custXrefId;
}
public String getCustFrdAlrtIn() {
return custFrdAlrtIn;
}
public void setCustFrdAlrtIn(String custFrdAlrtIn) {
this.custFrdAlrtIn = custFrdAlrtIn;
}
public String getCustSatisTrdCt() {
return custSatisTrdCt;
}
public void setCustSatisTrdCt(String custSatisTrdCt) {
this.custSatisTrdCt = custSatisTrdCt;
}
public String getGamOpenRvTrdCt() {
return gamOpenRvTrdCt;
}
public void setGamOpenRvTrdCt(String gamOpenRvTrdCt) {
this.gamOpenRvTrdCt = gamOpenRvTrdCt;
}
public String getCustExtnlDelinq90DayCt() {
return custExtnlDelinq90DayCt;
}
public void setCustExtnlDelinq90DayCt(String custExtnlDelinq90DayCt) {
this.custExtnlDelinq90DayCt = custExtnlDelinq90DayCt;
}
public String getCustExtnlDelinqIn() {
return custExtnlDelinqIn;
}
public void setCustExtnlDelinqIn(String custExtnlDelinqIn) {
this.custExtnlDelinqIn = custExtnlDelinqIn;
}}
Tested Response:
{"data":{"individuals":[{"cust_xref_id":"abf","cust_frd_alrt_in":"n","cust_satis_trd_ct":"4","gam_open_rv_trd_ct":"4","cust_extnl_delinq_90_day_ct":"1","cust_extnl_delinq_in":"y"}]}}
I am trying to call a Spring Cloud Data Flow REST Endpoint which is supposed to return a list of all the executions of a task whose name is passed in the input.
For starters, I ran the following URL in the browser :
http://dataflow-server.myhost.net/tasks/executions?task1225
The following JSON is shown on the browser :
{
"_embedded": {
"taskExecutionResourceList": [
{
"executionId": 2908,
"exitCode": 0,
"taskName": "task1225",
"startTime": "2021-06-25T18:40:24.823+0000",
"endTime": "2021-06-25T18:40:27.585+0000",
"exitMessage": null,
"arguments": [
"--spring.datasource.username=******",
"--spring.cloud.task.name=task1225",
"--spring.datasource.url=******",
"--spring.datasource.driverClassName=org.h2.Driver",
"key=******",
"batchId=20210625_025755702",
"--spring.cloud.data.flow.platformname=default",
"--spring.cloud.task.executionid=2908"
],
"jobExecutionIds": [],
"errorMessage": null,
"externalExecutionId": "task1225-kp7mvwkmll",
"parentExecutionId": null,
"resourceUrl": "Docker Resource [docker:internal.artifactrepository.myhost.net/myProject/myimage:0.1]",
"appProperties": {
"spring.datasource.username": "******",
"spring.cloud.task.name": "task1225",
"spring.datasource.url": "******",
"spring.datasource.driverClassName": "org.h2.Driver"
},
"deploymentProperties": {
"spring.cloud.deployer.kubernetes.requests.memory": "512Mi",
"spring.cloud.deployer.kubernetes.limits.cpu": "1000m",
"spring.cloud.deployer.kubernetes.limits.memory": "8192Mi",
"spring.cloud.deployer.kubernetes.requests.cpu": "100m"
},
"taskExecutionStatus": "COMPLETE",
"_links": {
"self": {
"href": "http://dataflow-server.myhost.net/tasks/executions/2908"
}
}
}
]
},
"_links": {
"first": {
"href": "http://dataflow-server.myhost.net/tasks/executions?page=0&size=20"
},
"self": {
"href": "http://dataflow-server.myhost.net/tasks/executions?page=0&size=20"
},
"next": {
"href": "http://dataflow-server.myhost.net/tasks/executions?page=1&size=20"
},
"last": {
"href": "http://dataflow-server.myhost.net/tasks/executions?page=145&size=20"
}
},
"page": {
"size": 20,
"totalElements": 2908,
"totalPages": 146,
"number": 0
}
}
Next, I tried to call the same REST endpoint through Java; however, no matter what I try, the response object seems to be empty with none of the attributes populated :
Approach 1 : Custom domain classes created to deserialize the response. (Did not work. Empty content recieved in response)
ParameterizedTypeReference<Resources<TaskExecutions>> ptr = new ParameterizedTypeReference<Resources<TaskExecutions>>() {
};
ResponseEntity<Resources<TaskExecutions>> entity = restTemplate.exchange(
"http://dataflow-server.myhost.net/tasks/executions?task1225",
HttpMethod.GET, null, ptr);
System.out.println(entity.getBody().getContent()); **//empty content**
Where, TaskExecutions domain is as follows :
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({ "taskExecutionResourceList" })
#JsonIgnoreProperties(ignoreUnknown = true)
public class TaskExecutions {
public TaskExecutions() {
}
#JsonProperty("taskExecutionResourceList")
List<TaskExecutionResource> taskExecutionResourceList = new ArrayList<>();
#JsonProperty("taskExecutionResourceList")
public List<TaskExecutionResource> getTaskExecutionResourceList() {
return taskExecutionResourceList;
}
#JsonProperty("taskExecutionResourceList")
public void setTaskExecutionResourceList(List<TaskExecutionResource> taskExecutionResourceList) {
this.taskExecutionResourceList = taskExecutionResourceList;
}
}
And TaskExecutionResource is as follows :
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"executionId",
"exitCode",
"taskName",
"startTime",
"endTime",
"exitMessage",
"arguments",
"jobExecutionIds",
"errorMessage",
"externalExecutionId",
"parentExecutionId",
"resourceUrl",
"appProperties",
"deploymentProperties",
"taskExecutionStatus",
"_links" })
#JsonIgnoreProperties(ignoreUnknown = true)
public class TaskExecutionResource {
#JsonProperty("executionId")
private Integer executionId;
#JsonProperty("exitCode")
private Integer exitCode;
#JsonProperty("taskName")
private String taskName;
#JsonProperty("startTime")
private String startTime;
#JsonProperty("endTime")
private String endTime;
#JsonProperty("exitMessage")
private Object exitMessage;
#JsonProperty("arguments")
private List<String> arguments = new ArrayList<String>();
#JsonProperty("jobExecutionIds")
private List<Object> jobExecutionIds = new ArrayList<Object>();
#JsonProperty("errorMessage")
private Object errorMessage;
#JsonProperty("externalExecutionId")
private String externalExecutionId;
#JsonProperty("parentExecutionId")
private Object parentExecutionId;
#JsonProperty("resourceUrl")
private String resourceUrl;
#JsonProperty("appProperties")
private AppProperties appProperties;
#JsonProperty("deploymentProperties")
private DeploymentProperties deploymentProperties;
#JsonProperty("taskExecutionStatus")
private String taskExecutionStatus;
#JsonProperty("_links")
private Links links;
#JsonProperty("executionId")
public Integer getExecutionId() {
return executionId;
}
#JsonProperty("executionId")
public void setExecutionId(Integer executionId) {
this.executionId = executionId;
}
#JsonProperty("exitCode")
public Integer getExitCode() {
return exitCode;
}
#JsonProperty("exitCode")
public void setExitCode(Integer exitCode) {
this.exitCode = exitCode;
}
#JsonProperty("taskName")
public String getTaskName() {
return taskName;
}
#JsonProperty("taskName")
public void setTaskName(String taskName) {
this.taskName = taskName;
}
#JsonProperty("startTime")
public String getStartTime() {
return startTime;
}
#JsonProperty("startTime")
public void setStartTime(String startTime) {
this.startTime = startTime;
}
#JsonProperty("endTime")
public String getEndTime() {
return endTime;
}
#JsonProperty("endTime")
public void setEndTime(String endTime) {
this.endTime = endTime;
}
#JsonProperty("exitMessage")
public Object getExitMessage() {
return exitMessage;
}
#JsonProperty("exitMessage")
public void setExitMessage(Object exitMessage) {
this.exitMessage = exitMessage;
}
#JsonProperty("arguments")
public List<String> getArguments() {
return arguments;
}
#JsonProperty("arguments")
public void setArguments(List<String> arguments) {
this.arguments = arguments;
}
#JsonProperty("jobExecutionIds")
public List<Object> getJobExecutionIds() {
return jobExecutionIds;
}
#JsonProperty("jobExecutionIds")
public void setJobExecutionIds(List<Object> jobExecutionIds) {
this.jobExecutionIds = jobExecutionIds;
}
#JsonProperty("errorMessage")
public Object getErrorMessage() {
return errorMessage;
}
#JsonProperty("errorMessage")
public void setErrorMessage(Object errorMessage) {
this.errorMessage = errorMessage;
}
#JsonProperty("externalExecutionId")
public String getExternalExecutionId() {
return externalExecutionId;
}
#JsonProperty("externalExecutionId")
public void setExternalExecutionId(String externalExecutionId) {
this.externalExecutionId = externalExecutionId;
}
#JsonProperty("parentExecutionId")
public Object getParentExecutionId() {
return parentExecutionId;
}
#JsonProperty("parentExecutionId")
public void setParentExecutionId(Object parentExecutionId) {
this.parentExecutionId = parentExecutionId;
}
#JsonProperty("resourceUrl")
public String getResourceUrl() {
return resourceUrl;
}
#JsonProperty("resourceUrl")
public void setResourceUrl(String resourceUrl) {
this.resourceUrl = resourceUrl;
}
#JsonProperty("appProperties")
public AppProperties getAppProperties() {
return appProperties;
}
#JsonProperty("appProperties")
public void setAppProperties(AppProperties appProperties) {
this.appProperties = appProperties;
}
#JsonProperty("deploymentProperties")
public DeploymentProperties getDeploymentProperties() {
return deploymentProperties;
}
#JsonProperty("deploymentProperties")
public void setDeploymentProperties(DeploymentProperties deploymentProperties) {
this.deploymentProperties = deploymentProperties;
}
#JsonProperty("taskExecutionStatus")
public String getTaskExecutionStatus() {
return taskExecutionStatus;
}
#JsonProperty("taskExecutionStatus")
public void setTaskExecutionStatus(String taskExecutionStatus) {
this.taskExecutionStatus = taskExecutionStatus;
}
#JsonProperty("_links")
public Links getLinks() {
return links;
}
#JsonProperty("_links")
public void setLinks(Links links) {
this.links = links;
}
}
Approach 2 : Add spring-cloud-data-flow-rest as a maven dependency in my project and use the TaskExectuionResource entity defined in this project. :
TaskExecutionResource.Page = restTemplate.getForObject("http://dataflow-server.myhost.net/tasks/executions?task1225",
TaskExecutionResource.Page.class);//**Empty content**
Question : How can I deserialize the response of the JSON returned by a rest enndpoint that is using HATEOAS? It seems like a very daunting task to get this to work.
Not sure how you constructed RestTemplate but it doesn't work as is with hateoas and there's some additional steps you need to do.
To get idea what we've done see ObjectMapper config. There's hal module and additional mixin's what mapper needs to be aware of for these things to work.
below written is my input json:
{
"apiData": [{
"apiName": "API",
"arguments": [{
"callFlow": "A"
}, {
"directoryName1": "CFAActivation"
}, {
"recDurationIVR1": "6000"
}, {
"numToDial": "*72"
}, {
"sleepDuration": "0000"
}],
"step": "1"
}]
}
//here "arguments" json array has dynamic json objects..
Respected Java Class According to your JSON
public class Arguments
{
private String callFlow;
public String getCallFlow ()
{
return callFlow;
}
public void setCallFlow (String callFlow)
{
this.callFlow = callFlow;
}
#Override
public String toString()
{
return "ClassPojo [callFlow = "+callFlow+"]";
}
}
public class ApiData
{
private Arguments[] arguments;
private String apiName;
private String step;
public Arguments[] getArguments ()
{
return arguments;
}
public void setArguments (Arguments[] arguments)
{
this.arguments = arguments;
}
public String getApiName ()
{
return apiName;
}
public void setApiName (String apiName)
{
this.apiName = apiName;
}
public String getStep ()
{
return step;
}
public void setStep (String step)
{
this.step = step;
}
#Override
public String toString()
{
return "ClassPojo [arguments = "+arguments+", apiName = "+apiName+", step = "+step+"]";
}
}
public class Jobj
{
private ApiData[] apiData;
public ApiData[] getApiData ()
{
return apiData;
}
public void setApiData (ApiData[] apiData)
{
this.apiData = apiData;
}
#Override
public String toString()
{
return "ClassPojo [apiData = "+apiData+"]";
}
}
Very weird problem with Jackson and JaxB Annotation. See the issue with incorrect JSON format.
public interface All {
}
#XmlJavaTypeAdapter(value=AnyAdapter.class, type=Any.class)
public interface Any {
public boolean isKnown();
}
#XmlJavaTypeAdapter(value=AnyAdapter.class, type=Any.class)
public abstract class Known implements Any {
#Override
#XmlTransient
public boolean isKnown() {
return false;
}
public Known(String type) {
super();
this.type = type;
}
String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public abstract void validate();
}
#XmlRootElement
public class Resource extends Known implements All{
public Resource(Integer resourceId, String resourceName) {
super("Resource");
this.resourceId = resourceId;
this.resourceName = resourceName;
}
public Integer getResourceId() {
return resourceId;
}
public void setResourceId(Integer resourceId) {
this.resourceId = resourceId;
}
public String getResourceName() {
return resourceName;
}
public void setResourceName(String resourceName) {
this.resourceName = resourceName;
}
Integer resourceId;
String resourceName;
#Override
public void validate() {
}
}
#XmlRootElement
public class ResourceList extends Known{
public ResourceList(String name) {
super("ResourceList");
this.name = name;
}
List<All> resourceList;
String name;
#XmlElements({
#XmlElement(name="resource", type=Resource.class),
})
public List<All> getResourceList() {
return resourceList;
}
public void setResourceList(List<All> resourceList) {
this.resourceList = resourceList;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public void validate() {
}
}
public class JacksonJaxbAdapterImpl {
public ObjectMapper initializeJackson() {
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.setSerializationInclusion(Include.NON_NULL);
// Need to use JaxB Annotation.
JaxbAnnotationModule module = new JaxbAnnotationModule();
mapper.registerModule(module);
SimpleModule mySimpleModule = new SimpleModule();
mySimpleModule.addSerializer(Long.class, new ToStringSerializer());
mySimpleModule.addSerializer(Double.class, new ToStringSerializer());
mapper.registerModule(mySimpleModule);
return mapper;
}
public void writeObject(Object target, Writer writer, boolean isPretty) throws Exception {
ObjectMapper mapper = initializeJackson();
try {
if(isPretty) {
mapper.writerWithDefaultPrettyPrinter().writeValue(writer,target);
} else {
mapper.writeValue(writer,target);
}
} catch (JsonGenerationException e) {
throw new RuntimeException(e);
} catch (JsonMappingException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public class JacksonJaxbAdapterTest {
public static void main(String[] args) throws Exception {
JacksonJaxbAdapterTest test = new JacksonJaxbAdapterTest();
ResourceList report = test.makeObject();
StringWriter jacksonWriter = new StringWriter();
JacksonJaxbAdapterImpl jacksonMainClass = new JacksonJaxbAdapterImpl();
jacksonMainClass.initializeJackson();
jacksonMainClass.writeObject(report, jacksonWriter, true);
System.out.println(jacksonWriter);
}
ResourceList makeObject() {
Resource primary = new Resource(12,"primary");
Resource secondary = new Resource(13, "secondary");
List<All> listofRes = new ArrayList<All>();
listofRes.add(primary);
listofRes.add(secondary);
ResourceList report = new ResourceList("Daily");
report.setResourceList(listofRes);
return report;
}
}
The JSON Output is with an extra unwanted "resource" tag.
{
"name" : "Daily",
"resourceList" : [ {
"resource" : {
"resourceId" : 12,
"resourceName" : "primary",
"type" : "Resource"
}
}, {
"resource" : {
"resourceId" : 13,
"resourceName" : "secondary",
"type" : "Resource"
}
} ],
"type" : "ResourceList"
}
Remove the #XmlElements Tag (in ResourceList.java) and leave it with #XmlElement tag only- the JSON becomes perfect without the "resource" tag. #XmlElement(name="resource", type=Resource.class)
{
"name" : "Daily",
"resource" : [ {
"resourceId" : 12,
"resourceName" : "primary",
"type" : "Resource"
}, {
"resourceId" : 13,
"resourceName" : "secondary",
"type" : "Resource"
} ],
"type" : "ResourceList"
}
Any Suggestion?
My JSON response has the following structure.
How should i parse it to store in a list in java with the structure maintained.
Does GSON provide a solution or should i use multidimensional array list?
I need to pass each of the tag array to a different view pager what approach should I follow.
{
"tag": [
{
"listing_count": 5,
"listings": [
{
"source": "source1",
"data": {
"image": "image1",
"name": "name1"
},
"name": "name1"
}
]
},
{
"listing_count": 5,
"listings": [
{
"source": "source2",
"data": {
"image": "imag2",
"name": "name2"
},
"name": "name2"
}
]
}
]
}
EDIT:
I have created the classes as suggested in the answer..I am having trouble creating the GSON response class.
This is what I have created:
public GsonRequest(int method, String url, Class<T> clazz,
Listener<T> listener, ErrorListener errorListener, Gson gson) {
super(Method.GET, url, errorListener);
this.mClazz = clazz;
this.mListener = listener;
mGson = gson;
}
The gson class should be something like this:
public class TagList {
ArrayList<Tag> tags;
public static class Tag {
int listing_count;
ArrayList<Listings> listings;
public int getListing_count() {
return listing_count;
}
public void setListing_count(int listing_count) {
this.listing_count = listing_count;
}
public ArrayList<Listings> getListings() {
return listings;
}
public void setListings(ArrayList<Listings> listings) {
this.listings = listings;
}
}
public static class Listings {
String source;
Data data;
String name;
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static class Data {
String image;
String name;
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}