Deserializing numerical fields with JSON and Spring RestTemplate - java

I'm calling a WS using Spring and Resttemplate, and trying to parse the JSON response to a custom object.
WS call snippet:
RestTemplate restTemplate = new RestTemplate();
String wsURL = "https://www.dummyws.com";
String accessToken = "dummyAccessToken"
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Accept", "*/*");
MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
HttpEntity<MultiValueMap<String, String>> wsRequest = new HttpEntity<MultiValueMap<String, String>>(map, headers);
ResponseEntity<GetPassengersData> wsResponse = restTemplate.exchange(wsURL, HttpMethod.GET, wsRequest, GetPassengersData.class);
GetPassengersData getPassengerData = wsResponse.getBody();
WS response snippet:
{
"data" : [{
"start" : "2018-01-01 00:00:00",
"finish" : "2018-01-01 23:59:59",
"total" : 1076
}
]
}
Custom object definition:
#JsonIgnoreProperties(ignoreUnknown = true)
public static class GetPassengersData implements Serializable
{
private static final long serialVersionUID = 1L;
public GetPassengersData()
{
}
private List<GetPassengersDataItem> data;
public List<GetPassengersDataItem> getData() {
return data;
}
public void setData(List<GetPassengersDataItem> data) {
this.data = data;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static class GetPassengersDataItem implements Serializable
{
private static final long serialVersionUID = 1L;
public GetPassengersDataItem()
{
}
private String start;
private String finish;
private double total;
public String getStart() {
return start;
}
public void setStart(String start) {
this.start = start;
}
public String getFinish() {
return finish;
}
public void setFinish(String finish) {
this.finish = finish;
}
public double getTotal() {
return total;
}
public void setTotal(double total) {
this.total = total;
}
}
I get the following exception in restTemplate.exchange:
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Can not deserialize instance of double out of START_OBJECT token
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#552b744a; line: 5, column: 44] (through reference chain: *.GetPassengersData["data"]->*.GetPassengersDataItem["total"]); nested exception is org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of double out of START_OBJECT token
Parsing just String fields works nicely. What I'm missing? What do I need to do to parse a numerical field? I've tried with int, long, double, BigDecimal, getting the same exception error.
Thanks in advance.

I'm not sure but try WS response without "data" node
{
[{
"start" : "2018-01-01 00:00:00",
"finish" : "2018-01-01 23:59:59",
"total" : 1076
}]
}
Or try to mappe to an object with a property List<GetPassengersData> data

Related

Case Sensitive POJO to Json mapping Hybris

How can I keep the original case of the keys when writing the object to json?
POJO-Class:
public class LeadRequest
{
private String AccountName;
private String AccountAlias;
private String BPID;
private String CustomerType;
private String Email;
private String LocationType;
private String APRID;
private String APRDistributorName;
private String EngagedwithRAOrDistributor;
public String getBPID()
{
return BPID;
}
public void setBPID(final String bPID)
{
BPID = bPID;
}
public String getEngagedwithRAOrDistributor()
{
return EngagedwithRAOrDistributor;
}
public void setEngagedwithRAOrDistributor(final String engagedwithRAOrDistributor)
{
EngagedwithRAOrDistributor = engagedwithRAOrDistributor;
}
}
Service-class:
public void submitLeadRequest(final LeadRequest lead)
{
try
{
final String endPoint = Config.getParameter(ServicesConstants.API_URL);
final HttpPost request = new HttpPost(endPoint);
request.addHeader(ServicesConstants.CONTENT_TYPE, ServicesConstants.APPLICATION_JSON);
request.addHeader(ServicesConstants.CLIENT_ID, Config.getParameter(ServicesConstants.CLIENT_ID));
request.addHeader(ServicesConstants.CLIENT_SECRET, Config.getParameter(ServicesConstants.CLIENT_SECRET));
final ObjectMapper mapper = new ObjectMapper();
final String jsonInString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(lead);
final StringEntity entity = new StringEntity(jsonString);
request.setEntity(entity);
final RequestConfig requestConfig = getRequestConfig(API_TIMEOUT_LONG);
final CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build();
CloseableHttpResponse response = client.execute(request);
}
}
Currently the Post Request json generated is:
{
"accountAlias" : "No Account Alias",
"accountName" : "REI AUTOMATION INC",
"customerType" : "OEM",
"aprid" : "002",
"bpid" : "0099105850",
"locationType" : "Research & Development",
"email" : "john.smith#jefftestaccount.com",
"engagedwithRAOrDistributor" : "",
"aprdistributorName" : "002-CED Royal Industrial Elec"
}
But the post request is failing giving HTTP/1.1 500 Server Error because of case sensitive keys in request json for the system being called
Therefore, the desired Request Json is:
If you are using com.fasterxml.jackson.databind.ObjectMapper, you can specify final name for each field using com.fasterxml.jackson.annotation.JsonProperty for example:
#JsonProperty("AccountName")
private String AccountName;
Or you can “tell” to your mapper to use fields instead of getters for creating a final JSON. In order to do so you can just configure your mapper class as follows:
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);

Java Jackson: Deserialize JSON with dynamic Names into JavaClass

I'm trying to deserialize JSON using jackson. The problem is that field names always change.
Here is an example JSON:
{
2021-08-02: [
{
label: "OnlineGallery",
nb_uniq_visitors: 1,
nb_visits: 2,
nb_events: 2,
nb_events_with_value: 0,
sum_event_value: 0,
min_event_value: 0,
max_event_value: 0,
avg_event_value: 0,
segment: "eventCategory==OnlineGallery",
idsubdatatable: 1
}
],
2021-08-03: [ ],
2021-08-04: [ ]
}
I´m getting the data by Resttemplate and try to deserialize into Java Class but it doesn't work:
private EventsGetCategory getEventFromAPI(Date startdate, Date enddate) {
RestTemplate restTemplate = new RestTemplate();
DateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd");
ResponseEntity<EventsGetCategory> response =
restTemplate.getForEntity(
matomoUrl + "index.php?module=API&label=OnlineGallery&method=Events.getCategory&secondaryDimension=eventAction&idSite=1&period=day&date=" + dateFormat.format(startdate) + "," + dateFormat.format(enddate) + "&format=JSON&filter_limit=-1"
+ "&token_auth=" + tokenAuth,
EventsGetCategory.class);
return response.getBody();
}
EventsGetCategory Class:
#Data
public class EventsGetCategory {
#JsonAnySetter
private Map<String, List<EventDetails>> details;
}
EventDetails Class:
#Data
public class EventDetails {
String label;
int nb_uniq_visitors;
int nb_visits;
int nb_events;
int nb_events_with_value;
int sum_event_value;
int min_event_value;
int max_event_value;
int avg_event_value;
String segment;
int idsubdatatable;
}
Can someone help me please?
Update Your EventsGetCategory class like this
public class EventsGetCategory {
private Map<String, List<EventDetails>> details;
#JsonAnySetter
public void setDetails(String key, List<EventDetails> val) {
if (this.details == null) {
this.details = new HashMap<>();
}
this.details.put(key, val);
}
}
if the field names are always changed then you can not use a class like EventDetails to map the JSON object to a java class, instead of EventDetails class, you have to use Map<String, Object> where you can store dynamic key values.

Get the value of items of a Json with Java Spring Boot

I'm trying to extract API data from the Json file below.
I want to retrieve the "name" of each "item".
Once the "name" is retrieved, I want to create a new Json that will contain :
{name: "toto", name: "titi"....}
The goal is then to create an API on my side which on a call from http://localhost/getitems will return the result of the Json created.
I'm new to Java and Spring Boot, so if you think there is a code that is easier, let me know, i hope you can help me to create that new Json file easily. Thanks !
// Json File (it has been reduced, more than 700 name are present)
{
"kind": "Space",
"apiVersion": "v1",
"metadata": {
"selfLink": "something",
"resourceVersion": "something"
},
"items": [
{
"metadata": {
"name": "projet1"
}
},
{
"metadata": {
"name": "com-cicd"
}
}
]
}
// TestGet.java Class
public static NameSpaceJson getPostWithCustomHeaders(String DebutUrl, String MilieuUrl, String ParamUrl) {
String url = DebutUrl.concat(MilieuUrl).concat(ParamUrl);
String Bearer = "...";
// create headers & template
HttpHeaders headers = new HttpHeaders();
RestTemplate restTemplate = new RestTemplate();
// set `accept` header for the type of response
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
// set custom header, bearer here too
headers.set("x-request-source", "desktop");
headers.set("Authorization", "Bearer "+Bearer);
// build the request
#SuppressWarnings({ "rawtypes", "unchecked" })
HttpEntity request = new HttpEntity(headers);
// use `exchange` method for HTTP call, this one permits us to use bearer for auth
ResponseEntity<NameSpaceJson> response = restTemplate.exchange(url, HttpMethod.GET, request, NameSpaceJson.class, 1);
if(response.getStatusCode() == HttpStatus.OK) {
return response.getBody();
} else {
return null;
}
}
// The name in the same file
public static void main(String[] args) {
TestGet.disableSSLCertificateChecking();
NameSpaceJson resultresponse = getPostWithCustomHeaders("https...","api","names");
// Long response = resultresponse.getValue().getId();
List<Item> response = resultresponse.getItems();
String test = GenerateNewJsonNameSpace.createJsonContent(response);
System.out.println(test);
}
//NameSpaceJson.java File
package com.example.consumingrest;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown = true)
public class NameSpaceJson {
private String kind;
private String apiVersion;
private List<Item> items;
public String getKind() {
return kind;
}
public void setKind(String kind) {
this.kind = kind;
}
public String getApiVersion() {
return apiVersion;
}
public void setApiVersion(String apiVersion) {
this.apiVersion = apiVersion;
}
public List<Item> getItems() {
return items;
}
public void setItems(List<Item> items) {
this.items = items;
}
}
//Metadata.java
package com.example.consumingrest;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown = true)
public class Metadata {
private String name;
private String creationTimestamp;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCreationTimestamp() {
return creationTimestamp;
}
public void setCreationTimestamp(String creationTimestamp) {
this.creationTimestamp = creationTimestamp;
}
}
//Item.java
package com.example.consumingrest;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown = true)
public class Item {
Metadata metadata;
public Metadata getMetadata() {
return metadata;
}
public void setMetadata(Metadata metadata) {
this.metadata = metadata;
}
}
// GenerateNewJsonNameSpace ( this is what i have tried.. but i'm sure we can do really better.. )
package com.example.consumingrest;
import java.util.List;
public class GenerateNewJsonNameSpace {
public static String createJsonContent(List<Item> ListOfNameSpace) {
if(ListOfNameSpace.isEmpty()) {
return null;
}else {
String LeJson;
LeJson = "{";
for(int i = 0; i < ListOfNameSpace.size(); i++) {
LeJson.concat(ListOfNameSpace.get(i).getMetadata().getName());
LeJson.concat(", \n");
}
LeJson.concat("}");
return LeJson;
}
}
}
you can use a library named Gson, which is created by google specifically for handling the JSON data.
All you need to do is create a new Gson object and parse the JSON with it.
You can do in just couple of lines
String jsonString = "{ \"kind\": \"Space\", \"apiVersion\": \"v1\", \"metadata\": { \"selfLink\": \"something\", \"resourceVersion\": \"something\" }, \"items\": [ { \"metadata\": { \"name\": \"projet1\" } }, { \"metadata\": { \"name\": \"affeccom-cicd\" } } ] }";
JsonObject data = new Gson().fromJson(jsonString, JsonObject.class);
JsonArray names = data .get("items").getAsJsonArray();
for(JsonElement element : names){
JsonObject object = element.getAsJsonObject();
System.out.println(object.get("metadata").getAsJsonObject().get("name").getAsString());
}

Nested JSON objects from Spring RestClient and Jackson

I have a rest client that will be getting JSON back from an endpoint. I would like to just get what is in the data[].
{
"responseStatus": "SUCCESS",
"responseDetails": {
"limit": 1000,
"offset": 0,
"size": 2,
"total": 2
},
"data": [
{
"id": "00P000000001M01",
"name__v": "Foo",
"status__v": [
"active__v"
],
"abbreviation__c": "F170053",
"internal_name__c": "Foo",
"therapeutic_area__c": [
"neurology__c"
],
"external_id__v": "84",
"generic_name__c": "Foo",
"scientific_name__c": null
},
{
"id": "00P000000001N01",
"name__v": "Bar",
"status__v": [
"active__v"
],
"abbreviation__c": "B333334",
"internal_name__c": "Bar",
"therapeutic_area__c": [
"bone_muscle_joint__c"
],
"external_id__v": "101",
"generic_name__c": "Bar",
"scientific_name__c": null
}
]
}
Because I will make other calls which will return different fields in the data[], I wanted to map each type to a POJO so I used #JSONProperty
#JsonIgnoreProperties(ignoreUnknown = true)
public class Product extends VBase{
private String fNumber;
private String genericName;
private String scientificName;
private String therapeuticArea;
public String getFNumber() {
return fNumber;
}
#JsonProperty("abbreviation__c")
private void unpackFNumber(Map<String,Object> abbreviation__c){
fNumber = ((Map<String,Object>)abbreviation__c.get("data")).get("abbreviation__c").toString();
}
public void setLyNumber(String fNumber) {
this.fNumber = fNumber;
}
public String getGenericName() {
return genericName;
}
#JsonProperty("generic_name__c")
private void unpackGenericName(Map<String,Object> generic_name__c){
genericName = ((Map<String,Object>)generic_name__c.get("data")).get("generic_name__c").toString();
}
public String getScientificName() {
return scientificName;
}
#JsonProperty("scientific_name__c")
private void unpackScientificName(Map<String,Object>sName){
scientificName = ((Map<String,Object>)sName.get("data")).get("scientific_name__c").toString();
}
public String getTherapeuticArea() {
return therapeuticArea;
}
#JsonProperty("therapeutic_area__c")
private void unpackTheraputicArea(Map<String,Object>tArea){
therapeuticArea=((Map<String,Object>)tArea.get("data")).get("therapeutic_area__c").toString();
}
}
I have tried various ways of just getting the data[].
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
RestTemplate restTemplate = new RestTemplate(clientHttpReq);
ResponseEntity<Product[]> response = restTemplate.exchange(url, HttpMethod.GET, request,Product[].class);
//ResponseEntity<Product[]> response = restTemplate.postForEntity(url,request,Product[].class ,parmMap);
What I have been trying to avoid is making a ResponseDetail POJO with a Data[] as a field. I know it will work but because the fields in the data[] will change based on the endpoint.
Can I use a wrapper class with a List to represent the data[] as all of the POJO will extend VBase?
Here is the exception:
Exception in thread "main" org.springframework.web.client.RestClientException: Error while extracting response for type [class [Lcom.lilly.models.Product;] and content type [application/json;charset=UTF-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `com.lilly.models.Product[]` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `com.lilly.models.Product[]` out of START_OBJECT token
at [Source: (PushbackInputStream); line: 1, column: 1]
Declaring a POJO class as below code, and then you can get data property from the POJO.
#Data
public class Example {
private String responseStatus;
private ResponseDetails responseDetails;
private List<Data> data;
#lombok.Data
public static class ResponseDetails {
private int limit;
private int offset;
private int size;
private int total;
}
#lombok.Data
public static class Data {
private String id;
private String name__v;
private String abbreviation__c;
private String internal_name__c;
private String external_id__v;
private String generic_name__c;
private Object scientific_name__c;
private List<String> status__v;
private List<String> therapeutic_area__c;
}
#lombok.Data
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Product {
#JsonProperty("name__v")
private String fNumber;
#JsonProperty("generic_name__c")
private String genericName;
#JsonProperty("scientific_name__c")
private String scientificName;
#JsonProperty("therapeutic_area__c")
private List<String> therapeuticArea;
}
public static void main(String[] args) {
String jsonResult = "{\"responseStatus\":\"SUCCESS\",\"responseDetails\":{\"limit\":1000,\"offset\":0,\"size\":2,\"total\":2},\"data\":[{\"id\":\"00P000000001M01\",\"name__v\":\"Foo\",\"status__v\":[\"active__v\"],\"abbreviation__c\":\"F170053\",\"internal_name__c\":\"Foo\",\"therapeutic_area__c\":[\"neurology__c\"],\"external_id__v\":\"84\",\"generic_name__c\":\"Foo\",\"scientific_name__c\":null},{\"id\":\"00P000000001N01\",\"name__v\":\"Bar\",\"status__v\":[\"active__v\"],\"abbreviation__c\":\"B333334\",\"internal_name__c\":\"Bar\",\"therapeutic_area__c\":[\"bone_muscle_joint__c\"],\"external_id__v\":\"101\",\"generic_name__c\":\"Bar\",\"scientific_name__c\":null}]}";
ObjectMapper objectMapper = new ObjectMapper();
try {
Example example = objectMapper.readValue(jsonResult, new TypeReference<Example>() {
});
//you can get data[] via example.getData()
System.out.println(objectMapper.writeValueAsString(example.getData()));
List<Product> products = objectMapper.readValue(objectMapper.writeValueAsString(example.getData()), new TypeReference<List<Product>>() {
});
//you can also parse data[] as List<Product>
System.out.println(JSONObject.toJSONString(products));
} catch (IOException e) {
e.printStackTrace();
}
}
}

Java Jackson deserialize an object containing a list of object with/without custom Deserializer?

I've got a JSON input like this
{
"slices": [{
"slice": {
"boundedBy": {
"Envelope": {
"axisLabels": "Lat Long ansi",
"lowerCorner": "-44.975 111.975 \"2003-01-01T00:00:00+00:00\"",
"upperCorner": "-8.975 155.975 \"2003-01-01T00:00:00+00:00\"",
"srsDimension": 3
}
},
"fileReferenceHistory": "/home/rasdaman/rasdaman_community/rasdaman/systemtest/testcases_services/test_all_wcst_import/testdata/wcs_local_metadata_tiff_no_specify_bands/GlobLAI-20030101-20030110-H01V06-1.0_MERIS-FR-LAI-HA.tiff",
"local_metadata_key": "value_1"
}
},
{
"slice": {
"boundedBy": {
"Envelope": {
"axisLabels": "Lat Long ansi",
"lowerCorner": "-44.975 111.975 \"2003-10-01T00:00:00+00:00\"",
"upperCorner": "-8.975 155.975 \"2003-10-01T00:00:00+00:00\"",
"srsDimension": 3
}
},
"fileReferenceHistory": "/home/rasdaman/rasdaman_community/rasdaman/systemtest/testcases_services/test_all_wcst_import/testdata/wcs_local_metadata_tiff_no_specify_bands/GlobLAI-20031001-20031010-H00V10-1.0_MERIS-FR-LAI-HA.tiff",
"local_metadata_key": "value_2"
}
}
],
"Title": "Drought code",
// other keys:values
}
with "slices" is an array of "slice" objects. Out of "slices" is any "keys":"values" but it is not the problem.
Then, I have a POJO class
public class CoverageMetadata {
#JsonProperty(value = "slices")
#JacksonXmlElementWrapper(useWrapping = false)
private List<LocalMetadata> localMetadataList;
private Map<String, String> globalMetadataAttributesMap;
#JsonAnySetter
public void addKeyValue(String key, String value) {
this.globalMetadataAttributesMap.put(key, value);
}
#JsonAnyGetter
public Map<String, String> getGlobalAttributesMap() {
return globalMetadataAttributesMap;
}
// other gettters, setters without Jackson annotations
}
and a class inside the list:
public class LocalMetadata {
public static final String LOCAL_METADATA_TAG = "slice";
private Map<String, String> localMetadataAttributesMap;
private BoundedBy boundedBy;
#JsonAnySetter
// NOTE: To map an unknown list of properties, must use this annotation
public void addKeyValue(String key, String value) {
this.localMetadataAttributesMap.put(key, value);
}
public LocalMetadata() {
this.localMetadataAttributesMap = new LinkedHashMap<>();
this.boundedBy = new BoundedBy();
}
#JsonAnyGetter
// NOTE: to unwrap the "map" from { "map": { "key": "value" } }, only keep { "key": "value" }
public Map<String, String> getLocalMetadataAttributesMap() {
return localMetadataAttributesMap;
}
public BoundedBy getBoundedBy() {
return this.boundedBy;
}
public void setBoundedBy(BoundedBy boundedBy) {
this.boundedBy = boundedBy;
}
public LocalMetadata(Map<String, String> localMetadataAttributesMap, BoundedBy boundedBy) {
this.localMetadataAttributesMap = localMetadataAttributesMap;
this.boundedBy = boundedBy;
}
}
And the basic code to deserialize JSON to object
ObjectMapper objectMapper = new ObjectMapper();
CoveageMetadata coverageMetadata = objectMapper.readValue(metadata, CoverageMetadata.class);
When I try to deserialize the JSON input to CoverageMetadata object, I got the error
Cannot deserialize coverage's metadata in XML/JSON by Jackson, error: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: {"slices":[{"slice":{"boundedBy":{"Envelope":{"axisLabels":"Lat Long ansi","srsDimension":3,"lowerCorner":"-44.975 111.975 \"2003-01-01T00:00:00+00:00\"","upperCorner":"-8.975 155.975 \"2003-01-01T00:00:00+00:00\""}},"local_metadata_key":"value_1","fileReferenceHistory":"/home/rasdaman/rasdaman_community/rasdaman/systemtest/testcases_services/test_all_wcst_import/testdata/wcs_local_metadata_tiff_no_specify_bands/GlobLAI-20030101-20030110-H01V06-1.0_MERIS-FR-LAI-HA.tiff"}}],"Title":"Drought code"}; line: 1, column: 21] (through reference chain: petascope.core.gml.metadata.model.CoverageMetadata["slices"]->java.util.ArrayList[0]->petascope.core.gml.metadata.model.LocalMetadata["slice"]).
How can I deserialize this JSON input String to CoverageMetadataObject with each "slice" element will be mapped to a LocalMetadata object?
The simple answer is I create another POJO class to hold the "slices" list, in CoverageMetadata class, it will have
public class CoverageMetadata {
private Map<String, String> globalMetadataAttributesMap;
#JsonProperty(value = "slices")
private LocalMetadata localMetadata;
...
}
New POJO class (class LocalMetadata before was renamed to LocalMetadataChild)
public class LocalMetadata {
#JsonProperty(value = "slice")
// This is the most important thing to avoid duplicate <slices><slices> when serializing in XML.
#JacksonXmlElementWrapper(useWrapping = false)
private List<LocalMetadataChild> localMetadataList;
public LocalMetadata(List<LocalMetadataChild> localMetadataList) {
this.localMetadataList = localMetadataList;
}
public LocalMetadata() {
this.localMetadataList = new ArrayList<>();
}
public List<LocalMetadataChild> getLocalMetadataList() {
return localMetadataList;
}
public void setLocalMetadataList(List<LocalMetadataChild> localMetadataList) {
this.localMetadataList = localMetadataList;
}
}

Categories