Jackson Serialize List Entities (add field to root list) - java

this my entity
#Entity
public class Product extends AbstractBaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Type(type = "objectid")
private String id;
private String title;
my resources
#Path(value = ApiConstant.Urls.PRODUCTS)
public class ProductResource {
#Inject
private ProductService productService;
#GET
#Path(value = ApiConstant.Urls.PRODUCTS)
#Produces(value = MediaType.APPLICATION_JSON)
public List getProducts(){
return productService.findAll();
}
my json response
[ {
"id" : "596b6a02f70a0878590bcf08",
"title" : "test1",
"description" : "description test 1"
}, {
"id" : "596b6b00f70a087b72d377eb",
"title" : "test1",
"description" : "description test 1"
}, {
"id" : "596b6b75f70a087d40f580d5",
"title" : "test1",
"description" : "description test 1"
} ]
I want to create a count field that counts the items in the list
like this and add the list to the results field
{
"count": 3,
"results": [
{
"id" : "596b6a02f70a0878590bcf08",
"title" : "test1",
"description" : "description test 1"
}, {
"id" : "596b6b00f70a087b72d377eb",
"title" : "test1",
"description" : "description test 1"
}, {
"id" : "596b6b75f70a087d40f580d5",
"title" : "test1",
"description" : "description test 1"
} ],
}
I want to serialize the Product List returned by jpa persistence

You can use the following class to include a count along with a list of Product entities:
public class ResultList {
private int count;
#JsonProperty("results") private List<Product> products;
public List<Product> getProducts() {
return products;
}
public void setProducts(List<Product> products) {
this.products = Objects.requireNonNull(products, "products");
this.count = products.size();
}
public int getCount() {
return count;
}
}

Class with generics type
public class ResultList<T> {
private int count;
#JsonProperty("results")
private List<T> items;
public List<T> getItems() {
return items;
}
public void setItems(List<T> items) {
this.items = Objects.requireNonNull(items, "items");
this.count = items.size();
}
public int getCount() {
return count;
}
}
ProductResource
#GET
#Path(value = ApiConstant.Urls.PRODUCTS)
#Produces(value = MediaType.APPLICATION_JSON)
public ResultList getProducts(){
List products = productService.findAll();
ResultList result = new ResultList<Product>();
result.setItems(products);
return result;
}
Thanks #ck1

Related

Infinite Loop Created When I want to Create an Organization Diagram Using Java Spring Boot

I try to do a project where I can show data Like this:
{
"vp2": {
"parentName": "ceo1",
"isParent": 0,
"hasChild": 0,
"child": []
}
}
Here My data are:
[
{
"entityName": "ceo1",
"parentId": 0,
"id": 1
},
{
"entityName": "ceo2",
"parentId": 0,
"id": 2
},
{
"entityName": "vp1",
"parentId": 1,
"id": 3
},
{
"entityName": "vp2",
"parentId": 1,
"id": 4
},
{
"entityName": "vp3",
"parentId": 1,
"id": 5
},
{
"entityName": "vp4",
"parentId": 2,
"id": 6
},
{
"entityName": "ceo3",
"parentId": 0,
"id": 7
},
{
"entityName": "ceo4",
"parentId": 0,
"id": 8
}
]
I want to show the data using map<Entity Name, DataModel>. Here DataModel's code is
#Data
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class DemoModel {
private String parentName;
private int isParent;
private int hasChild;
public List<Map<String, DemoModel>> child;
}
When there is no child, I get it properly. But when there is a child my function can not work. It gives infinite recursion problems. Here is my service function code
public Map<String, DemoModel> recursiveHelperFunction(int Id, List<DemoEntity> fullEntityList, List<Map<String, DemoModel>> modelList) {
Map<String, DemoModel> map = new HashMap<>();
DemoEntity demo = demoRepository.findById((long) Id).get();
DemoModel demoModel = new DemoModel();
String entityName = demo.getEntityName();
int parentId = (int) demo.getParentId();
if (Objects.equals(parentId, 0)) {
demoModel.setIsParent(1);
demoModel.setParentName("");
} else {
demoModel.setIsParent(0);
demoModel.setHasChild(0);
demoModel.setParentName(fullEntityList.get((int) demo.getParentId() - 1).getEntityName());
}
Iterator<DemoEntity> itr = fullEntityList.iterator();
while (itr.hasNext()) {
DemoEntity demo1 = itr.next();
int demo1Id = (int) demo1.getParentId();
int Id1 = (int) Id;
if (demo1Id == Id1) {
demoModel.setHasChild(1);
String demo1Entity = demo1.getEntityName();
try {
Map<String, DemoModel> map1 = recursiveHelperFunction((int) demo1.getId(), fullEntityList, modelList);
log.info("maps :{}", map1);
modelList.add(map1); //got error here
//demoModel.child.add(map1);
log.info("maps equal:??: {}", modelList.contains(map1));
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
demoModel.setChild(modelList);
map.put(entityName, demoModel);
return map;
}
I call this helper recursive function here:
public Map<String, DemoModel> getDataById(int Id) {
List<DemoEntity> list = demoRepository.findAll();
List<Map<String, DemoModel>> modelList = new ArrayList<>();
return recursiveHelperFunction(Id, list, modelList);
}
Here, the DemoEntity code is:
#Entity
#Data
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class DemoEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long Id;
#Column(unique = true)
private String entityName;
private long parentId;
}
How can I stop this infinite recursive problem?

jackson deserialize wrapped array

I got the following JSON that im trying to deserialize:
{
"items": [
{
"id": 29000012,
"name": "Crystal League I",
"iconUrls": {
"small": "https://api-assets.clashofclans.com/leagues/72/kSfTyNNVSvogX3dMvpFUTt72VW74w6vEsEFuuOV4osQ.png",
"tiny": "https://api-assets.clashofclans.com/leagues/36/kSfTyNNVSvogX3dMvpFUTt72VW74w6vEsEFuuOV4osQ.png",
"medium": "https://api-assets.clashofclans.com/leagues/288/kSfTyNNVSvogX3dMvpFUTt72VW74w6vEsEFuuOV4osQ.png"
}
},
{
"id": 29000015,
"name": "Master League I",
"iconUrls": {
"small": "https://api-assets.clashofclans.com/leagues/72/olUfFb1wscIH8hqECAdWbdB6jPm9R8zzEyHIzyBgRXc.png",
"tiny": "https://api-assets.clashofclans.com/leagues/36/olUfFb1wscIH8hqECAdWbdB6jPm9R8zzEyHIzyBgRXc.png",
"medium": "https://api-assets.clashofclans.com/leagues/288/olUfFb1wscIH8hqECAdWbdB6jPm9R8zzEyHIzyBgRXc.png"
}
}
],
"paging": {
"cursors": {}
}}
Im trying to deserialize it with the following DTO:
#JsonRootName("items")
#JsonIgnoreProperties(value={ "paging" })
public class League {
private Long id;
private String name;
private IconUrls iconUrls;
public League() {
}
}
class IconUrls {
private String small;
private String tiny;
private String medium;
public IconUrls() {
}
}
But im getting the following error:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Root name ('items') does not match expected ('List') for type `java.util.List<gg.stats.wrapper.entities.League>
I have also set: DeserializationFeature.UNWRAP_ROOT_VALUE
This is the call of the method from my Client:
List<League> getLeagueList();
The problem might be the "paging" key.
Any workaround for that?
I actually found a solution by myself:
#JsonIgnoreProperties(value={ "paging" }, allowGetters=true)
public class ResponseWrapper<T> {
private List<T> items;
#JsonProperty("items")
public List<T> getResponseContent() {
return this.items;
}
#JsonProperty("items")
public void setResponseContent(List<T> items) {
this.items = items;
}
}

How to add a list to an object in Spring

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);
}

Cannot parse Google geocode json object via webclient

I try to call following webclient query:
return webClient
.get()
.uri(uriBuilder -> uriBuilder
.path("/geocode/json")
.queryParam("key", google.getApiKey())
.queryParam("latlng", String.join(
",",
String.valueOf(point.getLat()),
String.valueOf(point.getLng()))
)
.build())
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(HttpStatus::isError, RestErrorHandler::manageError)
.bodyToMono(*PlacesSearchResponse.class*);
REST operation from google returns (https://developers.google.com/maps/documentation/geocoding/overview#GeocodingResponses) following object:
{
"results" : [
{
"address_components" : [
{
"long_name" : "1600",
"short_name" : "1600",
"types" : [ "street_number" ]
}
],
"formatted_address" : "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA",
"geometry" : {
"location" : {
"lat" : 37.4224764,
"lng" : -122.0842499
},
"location_type" : "ROOFTOP",
"viewport" : {
"northeast" : {
"lat" : 37.4238253802915,
"lng" : -122.0829009197085
},
"southwest" : {
"lat" : 37.4211274197085,
"lng" : -122.0855988802915
}
}
},
"place_id" : "ChIJ2eUgeAK6j4ARbn5u_wAGqWA",
"plus_code": {
"compound_code": "CWC8+W5 Mountain View, California, United States",
"global_code": "849VCWC8+W5"
},
"types" : [ "street_address" ]
}
],
"status" : "OK"
}
And problem is that I cannot parse it into my class: PlacesSearchResponse.class
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class PlacesSearchResponse {
public String status;
public String errorMessage;
#JsonProperty(value = "results")
public List<PlacesSearchResult> results;
}
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class PlacesSearchResult implements Serializable{
private static final long serialVersionUID = 1L;
#JsonProperty(value = "address_components")
public AddressComponent addressComponents[];
}
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class AddressComponent implements Serializable {
private static final long serialVersionUID = 1L;
public String longName;
public String shortName;
#JsonProperty(value = "types")
public String[] types;
}
I cannot find any mistake and webclient is ok, because when I tried bodyToMono(String.class) then I saw this object correctly.
I think you should parse it into an array, so do the following:
...
.bodyToMono(PlacesSearchResponse[].class);
Finnaly I have following call with objects:
.bodyToMono(PlacesSearchResponse.class);
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class PlacesSearchResponse {
public String status;
public String errorMessage;
List <PlacesSearchResult> results;
}
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class PlacesSearchResult implements Serializable{
#JsonProperty("place_id")
String placeId;
#JsonProperty("address_components")
List<AddressComponent> addressComponents;
#JsonProperty("formatted_address")
String formattedAddress;

Aggregation results cannot properly map to Java object

I'm trying to get schedules data from mongoDb.
I created the appropriate aggregation and tried to convert it within Spring Framework.
db.theaters.aggregate([
{ $match: { 'city_id': <someCityId>, 'theatreRooms.schedules.spectacle_id': <someSpecId> } },
{ $unwind: '$theatreRooms' },
{ $unwind: '$theatreRooms.schedules' },
{ $group: { _id: { name: '$name', room: '$theatreRooms.name' }, schedules: { $addToSet: '$theatreRooms.schedules.time' } } },
{ $group: { _id: '$_id.name', schedules: { $addToSet: { room: '$_id.room', schedules: '$schedules' } } } }
])
I've created properly match and unwind operations. But I've got problem with first group operation.
It seems that the operation is well interpreted, but for some reason I am not able to properly map the _id object.
Here is my code example:
public class TheaterProject {
private TheaterId _id;
private List<String> schedules;
public TheaterId get_id() {
return _id;
}
public void set_id(TheaterId _id) {
this._id = _id;
}
public List<String> getSchedules() {
return schedules;
}
public void setSchedules(List<String> schedules) {
this.schedules = schedules;
}
}
public class TheaterId {
#Field("name")
private String name;
#Field("room")
private Integer room;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getRoom() {
return room;
}
public void setRoom(Integer room) {
this.room = room;
}
}
public Document getRawSchedules(String cityId, String spectaclesId){
MatchOperation match = Aggregation.match(Criteria.where("city_id").is(cityId).and("theatreRooms.schedules.spectacle_id").is(spectaclesId));
UnwindOperation theaterUnwind = Aggregation.unwind("theatreRooms");
UnwindOperation schedulesUnwind = Aggregation.unwind("theatreRooms.schedules");
GroupOperation firstGroup = Aggregation.group(Fields.from(
Fields.field("name", "name"),
Fields.field("room", "theatreRooms.name")))
.addToSet("theatreRooms.schedules.time").as("schedules");
Aggregation agg = Aggregation.newAggregation(match,theaterUnwind,schedulesUnwind,firstGroup);
Document theaters = mongoTemplate.aggregate(agg, Theater.class, TheaterProject.class).getRawResults();
return theaters;
}
public List<TheaterProject> getSchedules(String cityId, String spectaclesId){
MatchOperation match = Aggregation.match(Criteria.where("city_id").is(cityId).and("theatreRooms.schedules.spectacle_id").is(spectaclesId));
UnwindOperation theaterUnwind = Aggregation.unwind("theatreRooms");
UnwindOperation schedulesUnwind = Aggregation.unwind("theatreRooms.schedules");
GroupOperation firstGroup = Aggregation.group(Fields.from(
Fields.field("name", "name"),
Fields.field("room", "theatreRooms.name")))
.addToSet("theatreRooms.schedules.time").as("schedules");
Aggregation agg = Aggregation.newAggregation(match,theaterUnwind,schedulesUnwind,firstGroup);
List<TheaterProject> theaters = mongoTemplate.aggregate(agg, Theater.class, TheaterProject.class).getMappedResults();
return theaters;
}
When I've invoked method getSchedules which return mapped objects, _id field is equal to null.
[
{
"_id": null,
"schedules": [
"5:15"
]
},
{
"_id": null,
"schedules": [
"6:55",
"4:35",
"10:15"
]
}
]
But when I've invoked getRawSchedules which used getRawResults it's looking properly.
{
"results": [
{
"_id": {
"name": "Pinokio",
"room": 2
},
"schedules": [
"5:15"
]
},
{
"_id": {
"name": "Roma",
"room": 1
},
"schedules": [
"6:55",
"4:35",
"10:15"
]
}
]
}
I don't have any idea why it's working like that.
I didn't find any information about this problem in the documentation and here. But I have a solution. You may just rename the field from _id to something else. theaterId for example. I don't know all requirements for your issue but you may do it just on mapping level.
Fix the mapping
import org.springframework.data.mongodb.core.mapping.Field;
import java.util.List;
public class TheaterProject {
#Field("theaterId")
private TheaterId _id;
private List<String> schedules;
public TheaterId get_id() {
return _id;
}
public void set_id(TheaterId _id) {
this._id = _id;
}
public List<String> getSchedules() {
return schedules;
}
public void setSchedules(List<String> schedules) {
this.schedules = schedules;
}
}
But it requires additional projection step
public List<TheaterProject> getSchedules(String cityId, String spectaclesId){
...
GroupOperation firstGroup = Aggregation.group(Fields.from(
Fields.field("name", "name"),
Fields.field("room", "theatreRooms.name")))
.addToSet("theatreRooms.schedules.time").as("schedules");
ProjectionOperation projection = Aggregation.project(Fields.from(
Fields.field("theaterId", "_id"),
Fields.field("schedules", "schedules")));
Aggregation agg = Aggregation.newAggregation( ... ,firstGroup, projection);
List<TheaterProject> theaters = mongoTemplate.aggregate(agg, "collectionName", TheaterProject.class).getMappedResults();
return theaters;
}

Categories

Resources