I'm trying to fix a problem I have with DynamoDB when creating a StaticTableSchema with Spring Native. Inside the cloudwatch logs I found this error:
Exception in thread "pool-4-thread-1" java.lang.ClassCastException: byte[] cannot be cast to com.beta80.movidapp.models.Event
DynamoDB table:
{
"id": {
"S": ""
},
"attendees": {
"L": [
{
"S": ""
},
{
"S": ""
}
]
},
"comments": {
"L": [
{
"M": {
"author": {
"S": ""
},
"comment_date": {
"S": ""
},
"comment_id": {
"S": ""
},
"content": {
"S": ""
}
}
}
]
}
}
Comment class:
#Data
#Builder(toBuilder = true)
#NoArgsConstructor
#AllArgsConstructor
public class Comment {
#Getter(onMethod_={#DynamoDbAttribute("comment_id")})
#Setter(onMethod_={#DynamoDbAttribute("comment_id")})
private String commentId;
private String content;
#Getter(onMethod_={#DynamoDbAttribute("comment_date")})
#Setter(onMethod_={#DynamoDbAttribute("comment_date")})
private String commentDate;
private String author;
}
Comment StaticTableSchema:
public class CommentMapper {
private TableSchema<Comment> tableSchema;
public CommentMapper() {
this.tableSchema = StaticTableSchema.builder(Comment.class)
.newItemSupplier(Comment::new)
.addAttribute(String.class, a -> a.name("commentId")
.getter(Comment::getCommentId)
.setter(Comment::setCommentId)
.tags(primaryPartitionKey()))
.addAttribute(String.class, a -> a.name("content")
.getter(Comment::getContent)
.setter(Comment::setContent))
.addAttribute(String.class, a -> a.name("commentDate")
.getter(Comment::getCommentDate)
.setter(Comment::setCommentDate))
.addAttribute(String.class, a -> a.name("author")
.getter(Comment::getAuthor)
.setter(Comment::setAuthor))
.build();
}
public TableSchema<Comment> getTableSchema() {
return tableSchema;
}
}
Event class:
#Data
#Builder(toBuilder = true)
#NoArgsConstructor
#AllArgsConstructor
public class Event {
#Getter(onMethod_={#DynamoDbPartitionKey})
private String id;
private List<String> attendees;
private List<Comment> comments;
}
EventMapper class:
public class EventMapper {
private TableSchema<Event> tableSchema;
public EventMapper() {
CommentMapper commentMapper = new CommentMapper();
this.tableSchema = StaticTableSchema
.builder(Event.class)
.newItemSupplier(Event::new)
.addAttribute(String.class, a -> a.name("id")
.getter(Event::getId)
.setter(Event::setId)
.tags(primaryPartitionKey()))
.addAttribute(EnhancedType.listOf(String.class), a -> a.name("attendees")
.getter(Event::getAttendees)
.setter(Event::setAttendees)
.attributeConverter(
ListAttributeConverter.builder(EnhancedType.listOf(String.class))
.collectionConstructor(ArrayList::new)
.elementConverter(StringAttributeConverter.create())
.build()))
.addAttribute(
EnhancedType.listOf(
EnhancedType.documentOf(Comment.class,commentMapper.getTableSchema()),
),
a -> a.name("comments")
.getter(Event::getComments)
.setter(Event::setComments)
)
.build();
}
public TableSchema<Event> getTableSchema() {
return tableSchema;
}
}
I have been trying to solve the problem for several days but I cannot find a solution. Be patient, I have just started developing in java recently.
Related
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?
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;
}
}
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'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;
}
I have a json:
{
"response": {
"GeoObjectCollection": {
"featureMember": [
{
"GeoObject": {
"description": "Country",
"name": "City",
"Point": {
"pos": "31.992615 45.057626"
}
}
},
{
"GeoObject": {
"description": "Country",
"name": "City",
"Point": {
"pos": "49.242414 49.895935"
}
}
}
]
}
}
}
I created DTO:
GeographicCoordinateDto.java:
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class GeographicCoordinateDto {
#JsonProperty("description")
private String location;
#JsonProperty("name")
private String cityName;
#JsonProperty("Point")
private GeographicCoordinatesDto geoCoordinates;
}
GeographicCoordinatesDto.java:
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class GeographicCoordinatesDto {
#JsonProperty("pos")
private String geoCoordinates;
}
Then I get JsonNode:
List<JsonNode> responseArrayOfObjects = mapper.readValue(new URL(yandexGeoCoderRestUrl+address), ObjectNode.class).findValues("GeoObject");
And I'm trying to convert to my DTO:
GeographicCoordinatesDto geo = mapper.convertValue(responseArrayOfObjects.get(0), GeographicCoordinatesDto.class);
But, I've null object:
GeographicCoordinatesDto(geoCoordinates=null)
What could be wrong?
UPDATE:
responseArrayOfObjects contains:
You are trying to get pos from the GeographicCoordinatesDto object, but it is inside the Point object of GeographicCoordinatesDto.
You can do this instead:
List<JsonNode> responseArrayOfObjects = mapper.readValue(new URL(yandexGeoCoderRestUrl+address), ObjectNode.class).findValues("Point");
or create another class for Point:
#JsonIgnoreProperties(ignoreUnknown = true)
class Point {
#JsonProperty("pos")
private String geoCoordinates;
}
and use it in GeographicCoordinatesDto:
#JsonIgnoreProperties(ignoreUnknown = true)
class GeographicCoordinatesDto {
#JsonProperty("Point")
private Point point;
}