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();
}
}
}
Related
I have a complex nested Json
It has a body similar to this:
{
staus: "Success",
id: 1,
data: [{'Movie':'kung fu panda','% viewed': 50.5},{'Movie':'kung fu panda 2','% viewed':1.5}],
metadata: {'filters':['Movie', 'Percentage Viewed'], 'params':{'content':'Comedy', 'type': 'Movie'}}
}
The only field I care about is data, and metadata is usually an even more complex/nested field. I was trying to map this to:
#JsonIgnoreProperties(ignoreUnknown = true)
class ResponseData{
public Data[] data;
#JsonIgnoreProperties(ignoreUnknown = true)
class Data{
public String Movie;
public double viewed;
}
}
I was looking at Jackson as an option and writing my own serializer and use JsonIgnore to discard the metadata but can't get around it.
Any suggestion on how this could be done?
You can use jackson-utils
public class Foo {
public static void main(String... args) {
ResponseData responseData1 = new ResponseData(
1,
"Success",
new ResponseData.Data[] {
new ResponseData.Data("kung fu panda", 50.5),
new ResponseData.Data("kung fu panda 2", 1.5) },
new ResponseData.Metadata(
new HashSet<>(Arrays.asList("Movie", "Percentage Viewed")),
new ResponseData.Metadata.Params("Comedy", "Movie"))
);
String json = JacksonUtils.prettyPrint().writeValue(responseData1);
System.out.println(json);
ResponseData responseData2 = JacksonUtils.readValue(json, ResponseData.class);
}
}
class ResponseData {
private int id;
private String status;
private Data[] data;
private Metadata metadata;
public ResponseData() {
}
public ResponseData(int id, String status, Data[] data, Metadata metadata) {
this.id = id;
this.status = status;
this.data = data;
this.metadata = metadata;
}
public static class Data {
#JsonProperty("Movie")
private String movie;
#JsonProperty("% viewed")
private double viewedPercents;
public Data() {
}
public Data(String movie, double viewedPercents) {
this.movie = movie;
this.viewedPercents = viewedPercents;
}
}
public static class Metadata {
private Set<String> filters;
private Params params;
public Metadata() {
}
public Metadata(Set<String> filters, Params params) {
this.filters = filters;
this.params = params;
}
public static class Params {
private String content;
private String type;
public Params() {
}
public Params(String content, String type) {
this.content = content;
this.type = type;
}
}
}
}
Console output:
{
"id" : 1,
"status" : "Success",
"data" : [ {
"Movie" : "kung fu panda",
"% viewed" : 50.5
}, {
"Movie" : "kung fu panda 2",
"% viewed" : 1.5
} ],
"metadata" : {
"filters" : [ "Movie", "Percentage Viewed" ],
"params" : {
"content" : "Comedy",
"type" : "Movie"
}
}
}
P.S. As an alternative, there is another util gson-utils with the same syntax.
I have a basic SpringBoot 2.1.5.RELEASE app. Using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file with some RestControllers.
In 1 of the controller this is the body I send:
{
"depositHotel": "xxx",
"destinationHotel": "aaa",
"depositHotelAmount": "0.2",
"destinationHotelAmount": "4",
"destinationAddress": [{
"address": "asdf",
"tag": ""
}],
"refundAddress": [{
"address": "pio",
"tag": ""
}]
}
so I create this class to use it as a RequestBody:
public class HotelswitchHotelOrderRequestBody {
public static class Builder {
private String depositHotel;
private String destinationHotel;
private Float depositHotelAmount;
private Float destinationHotelAmount;
private JSONObject destinationAddress;
private JSONObject refundAddress;
public Builder(String depositHotel, String destinationHotel) {
this.depositHotel = depositHotel;
this.destinationHotel = destinationHotel;
}
public Builder withDepositHotelAmount (Float depositHotelAmount) {
this.depositHotelAmount = depositHotelAmount;
return this;
}
public Builder withDestinationHotelAmount (Float destinationHotelAmount) {
this.destinationHotelAmount = destinationHotelAmount;
return this;
}
public Builder toDestinationAddress (JSONObject destinationAddress) {
this.destinationAddress = destinationAddress;
return this;
}
public Builder toRefundAddress (JSONObject refundAddress) {
this.refundAddress = refundAddress;
return this;
}
public HotelswitchHotelOrderRequestBody build(){
HotelswitchHotelOrderRequestBody order = new HotelswitchHotelOrderRequestBody();
order.depositHotel = this.depositHotel;
order.depositHotelAmount = this.depositHotelAmount;
order.destinationAddress = this.destinationAddress;
order.destinationHotel = this.destinationHotel;
order.destinationHotelAmount = this.destinationHotelAmount;
order.refundAddress = this.refundAddress;
return order;
}
}
private String depositHotel;
private String destinationHotel;
private Float depositHotelAmount;
private Float destinationHotelAmount;
private JSONObject destinationAddress;
private JSONObject refundAddress;
private HotelswitchHotelOrderRequestBody () {
//Constructor is now private.
}
public String getDepositHotel() {
return depositHotel;
}
public void setDepositHotel(String depositHotel) {
this.depositHotel = depositHotel;
}
public String getDestinationHotel() {
return destinationHotel;
}
public void setDestinationHotel(String destinationHotel) {
this.destinationHotel = destinationHotel;
}
public Float getDepositHotelAmount() {
return depositHotelAmount;
}
public void setDepositHotelAmount(Float depositHotelAmount) {
this.depositHotelAmount = depositHotelAmount;
}
public Float getDestinationHotelAmount() {
return destinationHotelAmount;
}
public void setDestinationHotelAmount(Float destinationHotelAmount) {
this.destinationHotelAmount = destinationHotelAmount;
}
public JSONObject getDestinationAddress() {
return destinationAddress;
}
public void setDestinationAddress(JSONObject destinationAddress) {
this.destinationAddress = destinationAddress;
}
public JSONObject getRefundAddress() {
return refundAddress;
}
public void setRefundAddress(JSONObject refundAddress) {
this.refundAddress = refundAddress;
}
}
But I have this error when receiving the object:
JSON parse error: out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `org.json.JSONObject` out of START_ARRAY token
JSONObject's representation in actual JSON is a hash i.e. {...}. In your json data you're providing an array of hashes [{...}] which is not the same. Judging from your domain I don't think it has to be multiple values, so you can just omit [] in your payload and if it does then the fields in your Java class can be defined as JSONArray.
However, I think you should go with defining an Address class and either using
private Address destinationAddress;
private Address refundAddress;
or if it indeed does have to be an object array
private List<Address> destinationAddresses;
private List<Address> refundAddresses;
I had a similar usecase , where I could not define the json to a POJO. Using com.fasterxml.jackson.databind.JsonNode instead of the JSONObject worked.
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;
}
}
I need to send a json to a web address where this json will be consumed, basically I have a list of Dto's that I need to turn into a Json (with jackson).
Some information must be passed in the header:
timestamp= time, key= blablabla, accesskey= bla bla bla
As I have no experience with spring, i need to know how to do the HTTP request using Spring boot
(is it a post?)
Here's what I've implemented so far:
Student Dto Class
public class StudentDto {
private String name;
private String RM;
private String RG;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRM() {
return RM;
}
public void setRM(String RM) {
this.RM = RM;
}
public String getRG() {
return RG;
}
public void setRG(String RG) {
this.RG = RG;
}
}
Turning my list of students into json
public String convertToJson(List obj) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
String jsonInString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
return jsonInString;
}
}
And this is the format json has to leave
{
"table": "student",
"rows":
[
{
"PersonID": 1,
"name": "Name",
"RM": "56656565",
"RG": "8787845-7",
},
{
"PersonID": 2,
"name": "Name",
"RM": "56656565",
"RG": "8787845-7"
}
]
}
below is and example of how to use rest template. You'll want to create a class for the request that has String table; and List<StudentDto> rows; as members
private static void createEmployee()
{
final String uri = "http://localhost:8080/springrestexample/employees";
MultiValueMap<String> headers = new MultiValueMap<>();
//set headers
HttpEntity<EmployeeVO> newEmployee = new HttpEntity<>(new EmployeeVO(-1, "Adam", "Gilly", "test#email.com"),headers);
RestTemplate restTemplate = new RestTemplate();
EmployeeVO result = restTemplate.postForObject( uri, newEmployee, EmployeeVO.class);
System.out.println(result);
}
*edit added headers
*edid look here for json formatting
you will also need to have the id field added to your object
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