Json:
{
"response": {
"count": 3,
"items": [
6651536,
20410167,
40345521
]
}
}
NOT WORK - FriendsDTO:
#Jacksonized
#Builder
#Value
public class FriendsDTO {
int count;
#JsonProperty("items")
List<Integer> friends;
}
WORK - FriendsDTO:
#Jacksonized
#Builder
#Value
public class FriendsDTO {
Response response;
#Jacksonized
#Builder
#Value
public static class Response {
int count;
#JsonProperty("items")
List<Integer> friends;
}
}
WebFluxClient request:
FriendsDTO friends = webFluxClient.get()
.uri(config.methodGetFriends(), builder -> {
builder.queryParam("access_token", accessToken);
builder.queryParam("v", config.version());
return builder.build();
})
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(FriendsDTO.class)
.log()
.block();
logger.info(friends.toString());
output WORK: FriendsDTO(response=FriendsDTO.Response(count=3, friends=[6651536, 20410167, 40345521]))
output NOT WORK: FriendsDTO(count=0, friends=null)
How can I get rid of this misunderstanding in the Response view for successful json parsing ?
Thats the expected behaviour
{
name : "name",
description : "description"
}
This can be written as
class <ClassName>{
private String name;
private String description;
}
The json does not carry the ClassName. Similarly in your case, the word Response should be a valid attribute, like how I defined name in this example.
So , if you want , this class to work
#Jacksonized
#Builder
#Value
public class FriendsDTO {
int count;
#JsonProperty("items")
List<Integer> friends;
}
You JSON should be changed from
{
"response": {
"count": 3,
"items": [
6651536,
20410167,
40345521
]
}
}
To
{
"count": 3,
"items": [
6651536,
20410167,
40345521
]
}
Related
I have problem while deserializing JSON to Java Object. I have models and views as shown below:
public class View {
public static class CreateIpSuccessResponse {}
public static class CreateIpErrorResponse {}
}
I use this views in this classes:
Root class:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class CreateIpResponse {
#JsonProperty(value = "pResponseCode")
#JsonView({View.CreateIpSuccessResponse.class, View.CreateIpErrorResponse.class})
private Object pResponseCode;
#JsonProperty(value = "pResponse")
#JsonView({View.CreateIpSuccessResponse.class, View.CreateIpErrorResponse.class})
private CreateIpPResponse createIpPResponse;
}
First subclass:
#Data
public class CreateIpPResponse {
#JsonProperty("Status")
#JsonView({View.CreateIpSuccessResponse.class, View.CreateIpErrorResponse.class})
private String status;
#JsonProperty("Result")
#JsonView({View.CreateIpSuccessResponse.class, View.CreateIpErrorResponse.class})
private CreateIpResult result;
#JsonProperty("responseCode")
#JsonView({View.CreateIpSuccessResponse.class, View.CreateIpErrorResponse.class})
private String responseCode;
}
Second subclass:
#Data
public class CreateIpResult {
#JsonProperty(value = "partyid")
#JsonView(View.CreateIpSuccessResponse.class)
private String partyId;
#JsonProperty(value = "Error")
#JsonView(View.CreateIpErrorResponse.class)
private String error;
}
Example of my json deserialization:
public CreateIpResponse createIp(CreateIpRequest createIpRequest) throws IOException, SQLException {
String pRequest = new ObjectMapper().writer().withDefaultPrettyPrinter().writeValueAsString(createIpRequest);
Map<String, Object> response = openAccountRepository.callProcedure(pRequest, "createClientIP");
BigDecimal responseCode = (BigDecimal) response.get("pResponseCode");
if (responseCode.equals(new BigDecimal("200"))) {
return mapper
.readerWithView(View.CreateIpSuccessResponse.class)
.forType(CreateIpResponse.class)
.readValue(mapper.writeValueAsString(response));
} else {
return mapper
.readerWithView(View.CreateIpErrorResponse.class)
.forType(CreateIpResponse.class)
.readValue(mapper.writeValueAsString(response));
}
}
When I deserialize CreateIpSuccessResponse view, I expect:
{
"pResponseCode": 200,
"pResponse": {
"Status": "OK",
"Result": {
"partyid": "98493305"
},
"responseCode": "200"
}
}
But I get:
{
"pResponseCode": 200,
"pResponse": {
"Status": "OK",
"Result": {
"partyid": "98493305",
"Error": null
},
"responseCode": "200"
}
}
and vice versa, when I deserialize CreateIpErrorResponse view, I expect:
{
"pResponseCode": 400,
"pResponse": {
"Status": "Error",
"Result": {
"Error": "Некорректная дата выпуска"
},
"responseCode": "200"
}
}
But I get:
{
"pResponseCode": 400,
"pResponse": {
"Status": "Error",
"Result": {
"partyid": null,
"Error": "Некорректная дата выпуска"
},
"responseCode": "200"
}
}
My question is why i don`t getting result that i need?
It seems that ObjectMapper is not ignoring null values when serializing objects. So when creating he object mapper use the setSerializationInclusion(Include.NON_NULL) method call to tell the ObjectMapper to ignore the null values:
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
You can find the details here
Edit: added the summarization of the solution as moken suggested in comments.
I'm trying to get responce from another API convert it to Java clases and then sent to the front end using Spring Boot
I have JSON response from external API like this
{
"status": {
"timestamp": "2023-01-31T14:06:45.210Z",
"error_code": 0,
"error_message": null,
},
"data": [
{
"id": 7982,
"name": "wc4qtz6py1i",
"tags": [
"40rcevshzab",
"3ja25pufu0z"
],
"quote": {
"USD": {
"price": 0.2,
},
"BTC": {
"price": 7159,
}
}
},
{
"id": 8742,
"name": "uhso98ca",
"tags": [
"84jsjsaesxx",
"sasdd5dda76"
],
"quote": {
"USD": {
"price": 6,
},
"BTC": {
"price": 1230,
}
}
}
]
}
I need to convert all of this to classes using Spring Boot. But how I should organize it?
The most question is about "Data" array.
For now I have something like this.
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
public class MainDTO{
#JsonProperty("status")
private Status status;
#JsonProperty("data")
private Data data;
}
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
public class Data {
Coin[] coins;
}
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
public class Coin{
#JsonProperty("id")
private Integer id;
#JsonProperty("name")
private String name;
private Map<String, Coin> quote;
}
But it doesn't work. I have error:
I have error
Tue Jan 31 16:13:45 EET 2023
There was an unexpected error (type=Internal Server Error, status=500).
Error while extracting response for type [class com.example.myCoolApp.entity.MainDTO] and content type [application/json;charset=utf-8]
org.springframework.web.client.RestClientException: Error while extracting response for type [class com.example.myCoolApp.entity.CryptoDTO] and content type [application/json;charset=utf-8]
You do not need a Data class because data field in your json is not an object - it is an array of objects, in your case it is an array of Coin objects.
Change it the next way:
public class MainDTO{
#JsonProperty("status")
private Status status;
#JsonProperty("data")
private List<Coin> data;
}
It's worth mentioning that the JSON in your example is incorrect due to extra commas. I presume the correct form should look like this:
{
"status": {
"timestamp": "2023-01-31T14:06:45.210Z",
"error_code": 0,
"error_message": null
},
"data": [
{
"id": 7982,
"name": "wc4qtz6py1i",
"tags": [
"40rcevshzab",
"3ja25pufu0z"
],
"quote": {
"USD": {
"price": 0.2
},
"BTC": {
"price": 7159
}
}
},
{
"id": 8742,
"name": "uhso98ca",
"tags": [
"84jsjsaesxx",
"sasdd5dda76"
],
"quote": {
"USD": {
"price": 6
},
"BTC": {
"price": 1230
}
}
}
]
}
As for classes I would suggest you the next:
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
public class MainDTO {
#JsonProperty("status")
private Status status;
#JsonProperty("data")
private List<Coin> coins;
}
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
public class Status {
#JsonProperty("timestamp")
private String timestamp;
#JsonProperty("error_code")
private Integer errorCode;
#JsonProperty("error_message")
private String errorMessage;
}
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
public class Coin {
#JsonProperty("id")
private Integer id;
#JsonProperty("name")
private String name;
#JsonProperty("tags")
private List<String> tags;
#JsonProperty("quote")
private Map<String, Quote> quote;
}
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
public class Quote {
#JsonProperty("price")
private Double price;
}
The "data" array in the JSON response is an array of coins, so the coins property in the MainDTO class should be of type List<Coin>.
I have been wondering what exactly I am doing wrong here. The response I am getting from my POJO class has a root property that I am unable to remove.
I have this JSON response:
{
"page": 1,
"per_page": 6,
"total": 12,
"total_pages": 2,
"data": [
{
"id": 1,
"name": "cerulean",
"year": 2000,
"color": "#98B2D1",
"pantone_value": "15-4020"
},
{
"id": 2,
"name": "fuchsia rose",
"year": 2001,
"color": "#C74375",
"pantone_value": "17-2031"
}
],
"support": {
"url": "https://reqres.in/#support-heading",
"text": "To keep ReqRes free, contributions towards server costs are appreciated!"
}
}
I converted JSON to these POJO classes and ignore properties not required for my test.
First POJO
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Datum{
public int id;
public String name;
public int year;
public String color;
public String pantone_value;
}
Second POJO
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Root {
#JsonIgnore
public int page;
#JsonIgnore
public int per_page;
#JsonIgnore
public int total;
#JsonIgnore
public int total_pages;
public ArrayList<Datum> data;
#JsonIgnore
public Support support;
}
Third POJO:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Support {
public String url;
public String text;
}
I want to get the properties in the Responses' Data list and convert it to a map, so I did this:
public void verify( List<Map<String, String>> myTest) { //myTest holds the expected response i want to use for my assertion
Root response = (resp.as(Root.class));
Map<String, Object> mapResponse = mapper.convertValue(response, new TypeReference<>() {
});
System.out.println(mapResponse);
}
Output:
{data=[{id=1, name=cerulean, year=2000, color=#98B2D1, pantone_value=15-4020}, {id=2, name=fuchsia rose, year=2001, color=#C74375, pantone_value=17-2031}, {id=3, name=true red, year=2002, color=#BF1932, pantone_value=19-1664}]}
The {data= root property (key) at beginning of the output is what I was trying to remove as it's making my assertion fail.
This is the output I would like:
[{id=1, name=cerulean, year=2000, color=#98B2D1, pantone_value=15-4020}, {id=2, name=fuchsia rose, year=2001, color=#C74375, pantone_value=17-2031}]
How can I convert the response's data format to get this?
You can convert only data list
List<Map<String, Object>> mapResponse = mapper.convertValue(response.getData(), new TypeReference<>() {
});
System.out.println(mapResponse);
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);
}
I made a code which add comments on my localhost:3000 but its parsing to much info i want to remove "commentModel" but if i remove it from CommentRq class i get errors
comment example:
{ "commentModel": { "comment": "komentarz", "date": "3/6/19 9:34 AM" }, "id": 1}
i want it to be { "comment": "komentarz", "date": "3/6/19 9:34 AM" }, "id": 1 }
CommentRq
#AllArgsConstructor
#NoArgsConstructor
#Data
#Builder
public class CommentRq {
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private CommentModel commentModel;
#AllArgsConstructor
#NoArgsConstructor
#Data
#Builder
public static class CommentModel {
#JsonProperty("comment")
String resourceName;
#JsonProperty("date")
String resourceNamed;
}
}
CommentBody
public class CommentBody {
Date now = new Date();
#JsonInclude(JsonInclude.Include.NON_NULL)
public CommentRq RequestCommentBody() {
return CommentRq.builder()
.commentModel(new CommentRq.CommentModel(
"komentarz",
(DateFormat.getInstance().format(now))
))
.build();
}
}
Here i create comment
Interface.PostComment postComment = Feign.builder()
.client(new OkHttpClient())
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.logger(new Slf4jLogger(Interface.PostComment.class))
.logLevel(Logger.Level.FULL)
.target(Interface.PostComment.class, "http://localhost:3000/comments/");
#When("i try to add a comment")
public void postComment() {
Map<String, Object> headermap = new HashMap<>();
headermap.put("Content-Type", "application/json");
CommentBody requestComment = new CommentBody();
CommentRes commentRes = postComment.postComment(headermap, requestComment.RequestCommentBody());
id = commentRes.getId();
LOGGER.info("Created: " + DateFormat.getInstance().format(now));
}
You can annotate your private CommentModel commentModel with #JsonUnwrapped. It will unwrap your commentModel object and write its fields to the root of the json. This will handle your specific case. But you can revise your request structure as well: put CommentModel fields into CommentRq and map CommentModel object to CommentRq object.