Given this response:
{
"data": {
"id": 38943,
"title": "Appetere Platonem Tempor Interesset Natum",
"description": "epicuri alia atqui",
"visibility": "PUBLIC",
"picture_url": null,
"logo_picture": null,
"owner_id": 51065,
"created_at": "2021-08-16T05:59:15.325Z",
"updated_at": "2021-08-16T05:59:15.325Z",
"counts": {
"subscriptions": 0,
"spots": 0,
"comments": 0,
"impressions": 0,
"respots": 0,
"attachments": 0
},
"user": {
"id": 51065,
"name": "Jon",
"screen_name": "jon",
"url": null,
"location": null,
"profile_picture": null,
"picture_url": null,
"header_picture": null,
"about": null,
"counts": {
"maps": 26
}
},
"map_settings": {
"editor_access": [
"can_none.map",
"can_create.spots",
"can_create.events",
"can_create.comments",
"can_create.attachments",
"can_create.collaborators"
],
"visitor_access": [
"can_none.map",
"can_none.spots",
"can_none.events",
"can_create.comments",
"can_create.attachments",
"can_none.collaborators"
],
"respotting_to_this_map": false
}
},
"meta": {
"code": 200
}
}
How could I deserialize the data property into a MapDTO like this
#JsonIgnoreProperties(ignoreUnknown = true)
#Data
#Builder
#ToString
#NoArgsConstructor
#AllArgsConstructor
public class MapDTO {
private Integer id;
private String title;
private String description;
private String visibility;
#JsonAlias("picture_url")
private String pictureUrl;
#JsonAlias("logo_picture")
private String logoPicture;
#JsonAlias("owner_id")
private Integer ownerId;
#JsonAlias("created_at")
private String createdAt;
#JsonAlias("updated_at")
private String updatedAt;
private MapCountsDTO counts;
#JsonAlias("map_settings")
private MapSettingsDTO mapSettings;
}
I've tried with a custom deserializer, but it looks cumbersome to get all the properties by name and then create a new MapDTO object based on those and the nested objects. If the structure changes I have to change the deserializer and the DTO itself. It would be easier to let Jackson do the deserialization starting from a given root. I'm using Jackson 2.12.4 through RestAssured 4.4.0. I'd like to avoid creating unnecessary wrapper classes.
Try to use jsonshema2pojo
This tool generates models for you.
All you need:
Copy and paste your JSON response to the window
Select on the radio group "Annotation style" radio button "Jackson 2.x"
Click on the "Preview" button
Profit.
Try this? https://json2csharp.com/json-to-pojo
// import com.fasterxml.jackson.databind.ObjectMapper; // version 2.11.1
// import com.fasterxml.jackson.annotation.JsonProperty; // version 2.11.1
/* ObjectMapper om = new ObjectMapper();
Root root = om.readValue(myJsonString), Root.class); */
public class Counts{
public int subscriptions;
public int spots;
public int comments;
public int impressions;
public int respots;
public int attachments;
public int maps;
}
public class User{
public int id;
public String name;
public String screen_name;
public Object url;
public Object location;
public Object profile_picture;
public Object picture_url;
public Object header_picture;
public Object about;
public Counts counts;
}
public class MapSettings{
public List<String> editor_access;
public List<String> visitor_access;
public boolean respotting_to_this_map;
}
public class Data{
public int id;
public String title;
public String description;
public String visibility;
public Object picture_url;
public Object logo_picture;
public int owner_id;
public Date created_at;
public Date updated_at;
public Counts counts;
public User user;
public MapSettings map_settings;
}
public class Meta{
public int code;
}
public class Root{
public Data data;
public Meta meta;
}
Related
I have following code
#Controller()
public class TestController {
#Get(value = "test", produces = MediaType.APPLICATION_JSON)
public MyDto fetch() throws Exception {
return new MyDto(
"test",
new ObjectMapper().readValue("{\"a\": 1}", ObjectNode.class)
);
}
#Serializable
#Data
public static class MyDto {
private final String name;
private final ObjectNode extraFields;
public MyDto(String name, ObjectNode extraFields) {
this.name = name;
this.extraFields = extraFields;
}
}
}
And I have an unexpected output on the client, extraFields object is empty
{
"name": "test",
"extraFields": [
[]
]
}
How to make Micronaut controller properly serialize com.fasterxml.jackson.databind.node.ObjectNode ?
I try to develop a api with spring-cloud-gateway but it's not easy for me
My "microservice A" return an Mono. This Object contains a List of id "Object B"
My "microservice B" return an Flux
In my api-gateway, how can i aggregate in a Mono the result of the microservices ?
#Service
public class ServiceAClient {
private final WebClient webClient;
public ServiceAClient(WebClient.Builder builder) {
this.webClient = builder.baseUrl("lb://microservice-A/A/").build();
}
public Mono<ObjectA> getObjectA(String id){
return webClient
.get()
.uri("{id}" , id)
.retrieve()
.bodyToMono(ObjectA.class)
.onErrorResume(ex->Mono.empty());
}
}
#Service
public class ServiceBClient {
private final WebClient webClient;
public ServiceAClient(WebClient.Builder builder) {
this.webClient = builder.baseUrl("lb://microservice-B/B/").build();
}
public Flux<ObjectB> getListObjectB(List<Long> ids){
return webClient
.get()
.uri("{ids}" , ids)
.retrieve()
.bodyToFlux(ObjectB.class);
}
}
#Data
public class ObjectA {
private UUID id;
private String name;
private String description;
private Date start;
private Date end;
private List<Long> listIdObjectB;
}
#Data
public class ObjectB {
private Long id;
private String name;
private String localisation;
}
#Data
public class MyDto {
private UUID id;
private String name;
private String description;
private Date start;
private Date end;
private List<ObjectB> listObjectB;
}
#Service
#AllArgsConstructor
public class CombinedService {
private final ServiceAClient serviceAClient;
private final ServiceBClient serviceBClient;
public Mono<MyDto> getDetails(String id){
// return MyDto who join a Mono Service A an Flux service B
}
}
The desired result
{
"id": "2355e7eb-edf7-428c-b51b-ac06c146ed3c",
"name": "toto",
"description": "lorem ipsum",
"debut": 01/06/2022,
"fin": 10/06/2022,
"ListObjectB": [
{
"id": 1,
"name": "foo",
"localisation": "here"
},
{
"id": 2,
"name": "bar",
"localisation": "here"
}
]
}
Thank you in advance for your help
You can take it as a start point to understand a possible solution.
I highly recommend you to read about reactive streams(https://www.reactive-streams.org/) and spring-webflux(https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html).
Your CombinedService should looks like:
#Service
#AllArgsConstructor
public class CombinedService {
private final ServiceAClient serviceAClient;
private final ServiceBClient serviceBClient;
public Mono<MyDto> getDetails(String id) {
return serviceAClient.getObjectA(id)
.map(objectA -> {
final Flux<ObjectB> fluxB = serviceBClient.getListObjectB(objectA.getListIdObjectB());
final List<ObjectB> listObjectB = fluxB.toStream().collect(Collectors.toList());
final MyDto myDto = new MyDto();
myDto.setName(objectA.getDescription());
myDto.setDescription(objectA.getDescription());
myDto.setListObjectB(listObjectB);
//More setters, etc
return myDto;
});
}
}
I have a Json Payload for a Post call as below:
{
"action" : "Closed",
"Id" : 30144,
"expireDate" : null,
"inputUser" : "abc",
"previousStatusId" : 1,
"statusId" : 4,
"Notes" : [ ]
}
My POJO classes for the above payload is as below
public class UpdateNoteStatus {
private String action;
private int Id;
private String expireDate;
private String inputUser;
private int previousStatusId;
private int statusId;
private List<Notes> Notes;
public void setAction(String action) {
this.action = action;
}
public void setId(int Id) {
this.Id = Id;
}
public void setExpireDate(String expireDate) {
this.expireDate = expireDate;
}
public void setinputUser(String inputUser) {
this.inputUser = inputUser;
}
public void setPreviousStatusId(int previousStatusId) {
this.previousStatusId = previousStatusId;
}
public void setStatusId(int statusId) {
this.statusId = statusId;
}
public void setNotes(List<Notes> Notes) {
this.Notes = Notes;
}
}
public class Notes{
}
Now I have assigned the values in the main class from where I am making the API call is as below:
ArrayList<Notes> Notes = new ArrayList<Notes>();
UpdateNoteStatus objUpdateNoteStatus = new UpdateNoteStatus();
objUpdateNoteStatus.setAction("Closed");
objUpdateNoteStatus.setId(Integer.parseInt("30144"));
objUpdateNoteStatus.setinputUser("abc");
objUpdateNoteStatus.setPreviousStatusId(1);
objUpdateNoteStatus.setStatusId(4);
objUpdateNoteStatus.setNotes(Notes);
But when I am making the API POST call it is throwing exception - "no serializer found for class and no properties discovered to create beanserializer". Could you please help. The Step is hightlighted in Bold.
RequestSpecification rs = given().contentType("application/json");
**rs = rs.body(objUpdateNoteStatus);** //In This Step I am getting the above mentioned Exception
Response res = rs.when().post("/UpdateStatus");
as you are initializing an empty object , you need to use below Annotation supported in below library
com.jayway.restassured.RestAssured;
#JsonIgnoreProperties(ignoreUnknown=true)
class UpdateNoteStatus
Assume the following REST resource:
#Path("/ActiveLeadTask")
//Also possible MediaType.APPLICATION_XML
#Produces(MediaType.APPLICATION_JSON)
public class ActiveLeadTask {
private #Inject ActiveLeadTaskBo activeLeadBo;
#GET
#Path("/getBo")
public ActiveLeadTaskBo getBo() {
return activeLeadBo;
}
}
////////////////////////////////////////////////////
#XmlRootElement
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
public class ActiveLeadTaskBo implements Serializable {
private static final long serialVersionUID = 1L;
private String firstName;
private String lastName;
private String phoneNumber;
private String phoneCountryCode;
private AtomicInteger accessCounterField = new AtomicInteger(0);
public ActiveLeadTaskBo() {
firstName = "test";
lastName = "test";
}
public int getAccessCounter() {
return accessCounterField.incrementAndGet();
}
public void setAccessCounter(int seed) {
accessCounterField.set(seed);
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
The REST response for getBo() JSON (but not for XML) returns the following:
{
"accessCounter": 1,
"firstName": "test",
"lastName": "test",
"metadata": {
"contextualInstance": {
"accessCounter": 2,
"firstName": "test",
"lastName": "test"
},
"instance": {
"accessCounter": 3,
"firstName": "test",
"lastName": "test"
}
}
}
The JSON response contains an additional "metadata" field - how can I configure the project to not generate it, or avoid generating it? The CDI container is Weld and the JSON serializer is provided by Yasson.
Two solutions are possible:
create a wrapper class, for example, ActiveLeadTaskBoInjectWrapper:
#Inject
ActiveLeadTaskBoInjectWrapper activeLeadBo;
activeLeadBo.getInstance();
workaround Weld specifics:
#Inject
Foo foo;
public void doSomething() {
if (foo instanceof WeldClientProxy) {
// foo is a proxy
((WeldClientProxy)foo).getMetadata().getContextualInstance()
} else {
// not a proxy
}
}
Depends on what JSON processing framework is used in your REST endpoint. For jsonb-api (jsr367) the only possible solution is to implement javax.json.bind.config.PropertyVisibilityStrategy to exclude generated properties from processing.
Below mentioned is the JSON string, resultString:
{
"imageMaps": [{
"crc": "c2c4",
"flags": "0",
"length": "117384",
"index": 1,
"version": "1.1.90ea",
"status": ""
}, {
"crc": "7601",
"flags": "8",
"length": "117592",
"index": 2,
"version": "1.1.90ed",
"status": ""
}],
"complete": true,
"nextBootImageVersion": "",
"lastKnownGoodImageVersion": "1.1.90ed",
"runningImageVersion": "1.1.90ed"
}
I want to get the same converted to the object of class A:
public class A {
private boolean complete;
private String message;
private String lastKnownGoodImageVersion;
private String nextBootImageVersion;
private String runningImageVersion;
private Map<String, B> imageMaps;
private List<B> images;
private MacID macId;
}
I am trying to convert the json to object of class A using the code :
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
A a = objectMapper.readValue(resultString, A.class);
Code for class B is:
public static class B {
public String version;
public int flags;
public int crc;
public long length;
public String index;
public String status;
}
But getting the exception :
com.fasterxml.jackson.databind.JsonMappingException: Can not
deserialize instance of java.util.LinkedHashMap out of START_ARRAY
token
You declared property imageMaps as a Map<String, B> in your class, but in your JSON imageMaps is an array of B. The deserialization should work if you change imageMaps to images in your JSON.