I have a problem with JSON object parsing. The JSON represents an order placed by user.
This had worked before, but adding additional Entity/DTO to project caused some problems. To be more specific, JSON looks as below:
{
"orderElements": [
{
"product": {
"id": 3,
"name": "xxx",
"description": "yyy",
"category": {
"id": 2,
"name": "xxx"
},
"price": 11,
"count": 1
},
"quantity": 1
}
],
"user": {
"id": 110,
"lastName": "xxx",
"firstName": "xxx",
"addressLine": "xxx",
"city": "xxx",
"country": "xxx",
"zipCode": "123456",
"phoneNumber": "1234567",
"password": "xxxx",
"email": "xxxx",
"roles": [
"USER"
]
},
"orderPlaceTime": null,
"deliveryAddress": {
"street": "xxx",
"city": "xxx",
"zipCode": "xxx"
}
}
If sent without "deliveryAddress" part, JSON is being parsed correctly and everything worked just fine. But trying to send JSON with "deliveryAddress" and all it's contents results in NullPointerException.
Although debugging frontend shows that whole JSON is filled correctly - street, city and zipCode fields contain all data that has been put by user (this above is exactly what is send by "POST" - "orderPlaceTime" is filled on backend side by LocalDateTime.now() function).
ORDER
#Entity
#Table(name = "carts")
public class Order extends AbstractEntity {
#Fetch(FetchMode.SELECT)
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "id_order")
#JsonIgnore
private Set<OrderElement> orderElements;
#Column
private LocalDateTime orderPlaceTime;
#ManyToOne
#JoinColumn(name = "id_user")
private User user;
#ManyToOne
#JoinColumn(name = "id_delivery")
private DeliveryAddress deliveryAddress;
public DeliveryAddress getDeliveryAddress() {
return deliveryAddress;
}
public void setDeliveryAddress(DeliveryAddress deliveryAddress) {
this.deliveryAddress = deliveryAddress;
}
public Set<OrderElement> getOrderElements() {
return orderElements;
}
public void setOrderElements(Set<OrderElement> orderElements) {
this.orderElements = orderElements;
}
public LocalDateTime getOrderPlaceTime() {
return orderPlaceTime;
}
public void setOrderPlaceTime(LocalDateTime orderPlaceTime) {
this.orderPlaceTime = orderPlaceTime;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
DELIVERYADDRESS
#Entity
#Table(name = "delivery_address")
public class DeliveryAddress extends AbstractEntity {
#Column
private String street;
#Column
private String city;
#Column
private String zipCode;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
ORDER CONVERTER
#Component
public class OrderConverter implements Converter<Order, OrderDTO> {
private final OrderElementConverter orderElementConverter;
private final DeliveryAddressConverter deliveryAddressConverter;
private final UserConverter userConverter;
public OrderConverter(OrderElementConverter orderElementConverter, DeliveryAddressConverter deliveryAddressConverter, UserConverter userConverter) {
this.orderElementConverter = orderElementConverter;
this.deliveryAddressConverter = deliveryAddressConverter;
this.userConverter = userConverter;
}
#Override
public Order convertToEntity(OrderDTO dto) {
Order order = new Order();
order.setId(dto.getId());
order.setUser(userConverter.convertToEntity(dto.getUser()));
order.setOrderPlaceTime(now());
Set<OrderElement> entitySet = new HashSet<>();
for (OrderElementDTO o : dto.getOrderElements()) {
entitySet.add(orderElementConverter.convertToEntity(o));
}
order.setOrderElements(entitySet);
// this below throws NullPointerException --> dto.getDeliveryAddressDTO();
order.setDeliveryAddress(deliveryAddressConverter.convertToEntity(dto.getDeliveryAddressDTO()));
return order;
}
}
DELIVERY ADRESS CONVERTER
#Component
public class DeliveryAddressConverter implements Converter<DeliveryAddress, DeliveryAddressDTO> {
#Override
public DeliveryAddress convertToEntity(DeliveryAddressDTO dto) {
DeliveryAddress deliveryAddress = new DeliveryAddress();
deliveryAddress.setId(dto.getId());
deliveryAddress.setCity(dto.getCity());
deliveryAddress.setZipCode(dto.getZipCode());
deliveryAddress.setStreet(dto.getStreet());
return deliveryAddress;
}
}
And I have no idea why deliveryAddress is null if it is sent by frontend correctly.
Thank you for all answers and suggestions
EDIT
It turned out as pointed by JB Nizet fields in OrderDTO had "deliveryAddressDTO" rather than "deliveryAddress". Cutting "DTO" from names fixed the issue:
public class OrderDTO extends AbstractDTO {
private Set<OrderElementDTO> orderElements;
private LocalDateTime orderPlaceTime;
private UserDTO user;
private DeliveryAddressDTO deliveryAddress;
// private DeliveryAddressDTO deliveryAddressDTO; <-- wrong name, methods also aligned
public DeliveryAddressDTO getDeliveryAddress() {
return deliveryAddress;
}
public void setDeliveryAddressDTO(DeliveryAddressDTO deliveryAddress) {
this.deliveryAddress = deliveryAddress;
}
public Set<OrderElementDTO> getOrderElements() {
return orderElements;
}
public void setOrderElements(Set<OrderElementDTO> orderElements) {
this.orderElements = orderElements;
}
public LocalDateTime getOrderPlaceTime() {
return orderPlaceTime;
}
public void setOrderPlaceTime(LocalDateTime orderPlaceTime) {
this.orderPlaceTime = orderPlaceTime;
}
public UserDTO getUser() {
return user;
}
public void setUser(UserDTO user) {
this.user = user;
}
}
It turned out as pointed by JB Nizet fields in OrderDTO had "deliveryAddressDTO" rather than "deliveryAddress". Cutting "DTO" from names fixed the issue:
public class OrderDTO extends AbstractDTO {
private Set<OrderElementDTO> orderElements;
private LocalDateTime orderPlaceTime;
private UserDTO user;
private DeliveryAddressDTO deliveryAddress;
// private DeliveryAddressDTO deliveryAddressDTO; <-- wrong name, methods also aligned
public DeliveryAddressDTO getDeliveryAddress() {
return deliveryAddress;
}
public void setDeliveryAddressDTO(DeliveryAddressDTO deliveryAddress) {
this.deliveryAddress = deliveryAddress;
}
public Set<OrderElementDTO> getOrderElements() {
return orderElements;
}
public void setOrderElements(Set<OrderElementDTO> orderElements) {
this.orderElements = orderElements;
}
public LocalDateTime getOrderPlaceTime() {
return orderPlaceTime;
}
public void setOrderPlaceTime(LocalDateTime orderPlaceTime) {
this.orderPlaceTime = orderPlaceTime;
}
public UserDTO getUser() {
return user;
}
public void setUser(UserDTO user) {
this.user = user;
}
}
Also id_delivery issue has been fixed by JB Nizet, as well - adding cascade.ALL annotation to the ManyToOne solved all problems.
Thank you!
Related
below is my api response
{
"skuInventory": {
"sku0": {
"inventory": {
"PODate": {
"time": 1674363600000
},
"checkBackOrderQTY": false,
"allocatedQuantity": 127,
"endDate": {
"time": 1669216432575
},
"balanceQuantity": 4096,
"ltr60Days": true,
"modifiedDate": null,
"availableQuantity": 0,
"id": "VS-1261",
"dateFirstReceived": {
"time": 1136178000000
},
"totalOnOrder": 4858,
"remainDaysCurrDatePODate": 52,
"pendingBackorders": 0,
"presellFlag": false,
"storeInventory": true
},
"quantityLimitWebPageMsg": "",
"freeShippingPromoAmt": 25,
"notCartableBrandOOSMsg": "",
"cartableFlags": {
"bopusOnlyMessage": "Item is unavailable for shipping, please check local stores for pickup availability",
"ADP": "0",
"BOPUS": "1",
"consumeUpdateFlexShippingVerbiage": "true",
"DTCEstShippingMessage": "Temporarily Out of Stock",
"isProductFlexShippingFeeApplied": "false",
"cartableSku": "1",
"DTC": "0",
"DTCAvailablityMessage": "Temporarily Out of Stock",
"isProductFlexShippingFeeWaived": "false",
"DTCEstShipMsgSiteExp": "Temporarily Out of Stock",
"DTCAvMsgPDPRedesign": "Temporarily Out of Stock"
},
"quantityThreshold": 0
}
}
}
As we can see from above json structure there are multiple properties like sku0,sku1.sku2
I want to convert this json into POJO which i have created which is like below,
public class Root {
private SkuInventory skuInventory;
public SkuInventory getSkuInventory() {
return skuInventory;
}
public void setSkuInventory(SkuInventory skuInventory) {
this.skuInventory = skuInventory;
}
}
public class SkuInventory {
//#JsonAlias({ "sku0", "sku1", "sku2" })
private List<Sku0> sku0;
public List<Sku0> getSku0() {
return sku0;
}
public void setSku0(List<Sku0> sku0) {
this.sku0 = sku0;
}
}
public class Sku {
private Inventory inventory;
private String quantityLimitWebPageMsg;
private int freeShippingPromoAmt;
private String notCartableBrandOOSMsg;
private CartableFlags cartableFlags;
private int quantityThreshold;
public Inventory getInventory() {
return inventory;
}
public void setInventory(Inventory inventory) {
this.inventory = inventory;
}
public String getQuantityLimitWebPageMsg() {
return quantityLimitWebPageMsg;
}
public void setQuantityLimitWebPageMsg(String quantityLimitWebPageMsg) {
this.quantityLimitWebPageMsg = quantityLimitWebPageMsg;
}
public int getFreeShippingPromoAmt() {
return freeShippingPromoAmt;
}
public void setFreeShippingPromoAmt(int freeShippingPromoAmt) {
this.freeShippingPromoAmt = freeShippingPromoAmt;
}
public String getNotCartableBrandOOSMsg() {
return notCartableBrandOOSMsg;
}
public void setNotCartableBrandOOSMsg(String notCartableBrandOOSMsg) {
this.notCartableBrandOOSMsg = notCartableBrandOOSMsg;
}
public CartableFlags getCartableFlags() {
return cartableFlags;
}
public void setCartableFlags(CartableFlags cartableFlags) {
this.cartableFlags = cartableFlags;
}
public int getQuantityThreshold() {
return quantityThreshold;
}
public void setQuantityThreshold(int quantityThreshold) {
this.quantityThreshold = quantityThreshold;
}
}
public class Inventory {
#JsonProperty("PODate")
private Time pODate;
private boolean checkBackOrderQTY;
private int allocatedQuantity;
private Time endDate;
private int balanceQuantity;
private boolean ltr60Days;
private Object modifiedDate;
private int availableQuantity;
private String id;
private Time dateFirstReceived;
private int totalOnOrder;
private int remainDaysCurrDatePODate;
private int pendingBackorders;
private boolean presellFlag;
private boolean storeInventory;
public Time getpODate() {
return pODate;
}
public void setpODate(Time pODate) {
this.pODate = pODate;
}
public boolean isCheckBackOrderQTY() {
return checkBackOrderQTY;
}
public void setCheckBackOrderQTY(boolean checkBackOrderQTY) {
this.checkBackOrderQTY = checkBackOrderQTY;
}
public int getAllocatedQuantity() {
return allocatedQuantity;
}
public void setAllocatedQuantity(int allocatedQuantity) {
this.allocatedQuantity = allocatedQuantity;
}
public Time getEndDate() {
return endDate;
}
public void setEndDate(Time endDate) {
this.endDate = endDate;
}
public int getBalanceQuantity() {
return balanceQuantity;
}
public void setBalanceQuantity(int balanceQuantity) {
this.balanceQuantity = balanceQuantity;
}
public boolean isLtr60Days() {
return ltr60Days;
}
public void setLtr60Days(boolean ltr60Days) {
this.ltr60Days = ltr60Days;
}
public Object getModifiedDate() {
return modifiedDate;
}
public void setModifiedDate(Object modifiedDate) {
this.modifiedDate = modifiedDate;
}
public int getAvailableQuantity() {
return availableQuantity;
}
public void setAvailableQuantity(int availableQuantity) {
this.availableQuantity = availableQuantity;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Time getDateFirstReceived() {
return dateFirstReceived;
}
public void setDateFirstReceived(Time dateFirstReceived) {
this.dateFirstReceived = dateFirstReceived;
}
public int getTotalOnOrder() {
return totalOnOrder;
}
public void setTotalOnOrder(int totalOnOrder) {
this.totalOnOrder = totalOnOrder;
}
public int getRemainDaysCurrDatePODate() {
return remainDaysCurrDatePODate;
}
public void setRemainDaysCurrDatePODate(int remainDaysCurrDatePODate) {
this.remainDaysCurrDatePODate = remainDaysCurrDatePODate;
}
public int getPendingBackorders() {
return pendingBackorders;
}
public void setPendingBackorders(int pendingBackorders) {
this.pendingBackorders = pendingBackorders;
}
public boolean isPresellFlag() {
return presellFlag;
}
public void setPresellFlag(boolean presellFlag) {
this.presellFlag = presellFlag;
}
public boolean isStoreInventory() {
return storeInventory;
}
public void setStoreInventory(boolean storeInventory) {
this.storeInventory = storeInventory;
}
}
Below is the codee to map json into a POJO
ResponseEntity<?> inventoryData = (ResponseEntity<?>) inventoryAPIResponse.getData();
Map<?, ?> inventoryBody = (Map<?, ?>) inventoryData.getBody();
Root atgInventory = getObjectMapper().convertValue(inventoryBody, Root.class);
Sku availableQuantity = (Sku) atgInventory.getSkuInventory().getSku();
If we execute above code we get errors like this
java.lang.IllegalArgumentException: Cannot deserialize value of type `java.util.ArrayList<Sku>` from Object value (token `JsonToken.START_OBJECT`)
at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: Root["skuInventory"]->SkuInventory["sku0"])
How can wee resolve this issue of mapping a json to a pojo which has a property which is dynamic like sku0,sku1,sku2 etc??
I would introduce a Map as follows:
public class Root {
private Map<String, Sku> skuInventory;
}
This Map will then replace your SkuInventory object and has keys like Sku0, Sku1 etc.
I am having an unusual error, when doing a get in postman, my method returns some values called target and targetClass, I don't know why, since I am implementing an interface just to bring some specific values, if you could help would be very appreciated
This is the model
#Entity
#Table(name = "tudb_users")
public class Tudb_users {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private int userId;
#Column(name = "client_id")
private Integer clientId;
#Column(name = "name")
private String name;
#Column(name = "last_name")
private String lastName;
#Column(name = "rol_id")
private Integer rolId;
#Column(name = "clinic_id")
private Integer clinicId;
#Column(name = "password")
private String password;
#Column(name = "rfc")
private String rfc;
#Column(name = "gender")
private Integer gender;
#Column(name = "date_born")
private String dateBorn;
#Column(name = "email")
private String email;
#Column(name = "phone")
private String phone;
#Column(name = "identification_card")
private String identificationCard;
#Column(name = "photo")
private String photo;
#Column(name = "folder")
private String folder;
#Column(name = "session")
private Integer session;
#Column(name = "promotion_code")
private String promotionCode;
#Column(name = "last_session")
#Temporal(TemporalType.TIMESTAMP)
#JsonFormat(pattern = "yy-MM-dd' 'HH:mm")
private Date lastSession;
#Column(name = "token")
private String token;
#Column(name = "token_email")
private String tokenEmail;
#Column(name = "status")
private Integer status;
#Column(name = "user_register")
private Integer userRegister;
#Column(name = "date_register")
#Temporal(TemporalType.TIMESTAMP)
#JsonFormat(pattern = "yy-MM-dd' 'HH:mm")
private Date dateRegister;
#Column(name = "user_update")
private Integer userUpdate;
#Column(name = "date_update")
#Temporal(TemporalType.TIMESTAMP)
#JsonFormat(pattern = "yy-MM-dd' 'HH:mm")
private Date dateUpdate;
#Column(name = "options")
private Integer options;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public Integer getClientId() {
return clientId;
}
public void setClientId(Integer clientId) {
this.clientId = clientId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Integer getRolId() {
return rolId;
}
public void setRolId(Integer rolId) {
this.rolId = rolId;
}
public Integer getClinicId() {
return clinicId;
}
public void setClinicId(Integer clinicId) {
this.clinicId = clinicId;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRfc() {
return rfc;
}
public void setRfc(String rfc) {
this.rfc = rfc;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public String getDateBorn() {
return dateBorn;
}
public void setDateBorn(String dateBorn) {
this.dateBorn = dateBorn;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getIdentificationCard() {
return identificationCard;
}
public void setIdentificationCard(String identificationCard) {
this.identificationCard = identificationCard;
}
public String getPhoto() {
return photo;
}
public void setPhoto(String photo) {
this.photo = photo;
}
public String getFolder() {
return folder;
}
public void setFolder(String folder) {
this.folder = folder;
}
public Integer getSession() {
return session;
}
public void setSession(Integer session) {
this.session = session;
}
public Date getLastSession() {
return lastSession;
}
public void setLastSession(Date lastSession) {
this.lastSession = lastSession;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Integer getUserRegister() {
return userRegister;
}
public void setUserRegister(Integer userRegister) {
this.userRegister = userRegister;
}
public Date getDateRegister() {
return dateRegister;
}
public void setDateRegister(Date dateRegister) {
this.dateRegister = dateRegister;
}
public Integer getUserUpdate() {
return userUpdate;
}
public void setUserUpdate(Integer userUpdate) {
this.userUpdate = userUpdate;
}
public Date getDateUpdate() {
return dateUpdate;
}
public void setDateUpdate(Date dateUpdate) {
this.dateUpdate = dateUpdate;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getTokenEmail() {
return tokenEmail;
}
public void setTokenEmail(String tokenEmail) {
this.tokenEmail = tokenEmail;
}
public Integer getOptions() {
return options;
}
public void setOptions(Integer options) {
this.options = options;
}
public String getPromotionCode() {
return promotionCode;
}
public void setPromotionCode(String promotionCode) {
this.promotionCode = promotionCode;
}
}
Now this is repository
#Repository
public interface TudbUsersRepository extends JpaRepository<Tudb_users, Long>{
#Query(value = "SELECT tudb_users.user_id, tudb_users.client_id, tu_clients.name as nameCliente, tudb_users.clinic_id, tudb_users.rol_id, tudb_roles.description as rolName, tudb_users.name, tudb_users.last_name,\r\n"
+ "tudb_users.email, tudb_users.photo, tudb_users.rfc, tudb_users.gender, tudb_users.date_born, tudb_users.folder, tudb_users.token\r\n"
+ "FROM dentabash_dev.tudb_users inner join tu_clients on tu_clients.client_id = tudb_users.client_id\r\n"
+ "inner join tudb_roles on tudb_roles.rol_id = tudb_users.rol_id where tudb_users.client_id = ?1", nativeQuery = true)
List<DentaUser> getUsersByClientId(Integer id);
}
This is the interface
public interface DentaUser {
Integer getUser_id();
Integer getClient_id();
Integer getClinic_id();
Integer getRol_id();
String getNameCliente();
String getRolName();
String getName();
String getLast_name();
String getEmail();
String getPhoto();
String getRfc();
Integer getGender();
#Temporal(TemporalType.TIMESTAMP)
#JsonFormat(pattern = "yy-MM-dd' 'HH:mm")
Date getDate_born();
String getFolder();
String getToken();
}
This is the service
#Service
public class TudbUsersServices {
#Autowired
private TudbUsersRepository repo;
public ResponseEntity<?> getUserByClientID(Integer id){
JSONObject response = new JSONObject();
try {
response.put("code", 200);
response.put("success", true);
response.put("data", repo.getUsersByClientId(id));
return new ResponseEntity<>(response.toString(), HttpStatus.OK);
}catch(Exception e) {
response.put("code", 400);
response.put("success", false);
response.put("message", "Bad request" + e.getMessage());
return new ResponseEntity<>(response.toString(), HttpStatus.OK);
}
}
}
And this is controller:
#RestController
#RequestMapping("/dbusers")
#Tag(name = "DentaBash Users", description = "DentaBash Users - Controller")
#CrossOrigin(origins = "*", methods = {RequestMethod.GET, RequestMethod.POST})
public class TudbUsersController {
#Autowired
private TudbUsersServices service;
#RequestMapping(value = "/usersByClient", method = RequestMethod.GET)
public ResponseEntity<?> getUserByClientID(#RequestParam Integer clientId){
return service.getUserByClientID(clientId);
}
}
Now I get this in postman:
{
"code": 200,
"data": [
{
"gender": 0,
"photo": "images/user.png",
"last_name": "Ortiz",
"client_id": 2,
"rfc": "TESTRFC",
"target": {
"gender": 0,
"last_name": "Ortiz",
"photo": "images/user.png",
"client_id": 2,
"rfc": "TESTRFC",
"token": "5mi7oit016l768aaj1mecgmgu6",
"folder": "images/test/folder",
"user_id": 4,
"date_born": "2022-02-01",
"name": "Juan",
"nameCliente": "Jose Luis Miranda Pavon",
"rol_id": 4,
"rolName": "Cirujano Dentista - Gerente",
"clinic_id": 1,
"email": "juan#gmail.com"
},
"token": "5mi7oit016l768aaj1mecgmgu6",
"folder": "images/test/folder",
"user_id": 4,
"targetClass": "class org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap",
"name": "Juan",
"nameCliente": "Jose Luis Miranda Pavon",
"rol_id": 4,
"rolName": "Cirujano Dentista - Gerente",
"clinic_id": 1,
"email": "juan#gmail.com"
}
],
"success": true
}
I can't seem to figure out how to add an entity that has a foreign key, though JSON.
I have a user model, and a post model.
A user can make different posts on a website.
This is a many-to-one relationship.
A user can have several posts, while a post can only have one user (the poster).
The post as a foreign key representing the id of the user that made the post.
This is the User model:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Table(name = "user")
public class User {
//ID
#Id
#SequenceGenerator(
name = "user_sequence",
sequenceName = "user_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.IDENTITY,
generator = "user_generator"
)
#Column(name = "id",nullable = false)
private int id;
private String username;
private String password;
private String email;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd")
#Column(name = "creation_date")
private Date creationDate;
//RELATIONSHIP
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private List<Post> posts = new ArrayList<>();
/* =========== GETTERS AND SETTERS ===========*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public List<Post> getPosts() {
return posts;
}
public void setPosts(List<Post> posts) {
this.posts = posts;
}
}
This is the Post model:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Table(name = "post")
public class Post {
//ID
#Id
#SequenceGenerator(
name = "post_sequence",
sequenceName = "post_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.IDENTITY,
generator = "post_generator"
)
#Column(name = "id", nullable = false)
private int id;
#Column(name = "post_content")
private String postContent;
private String title;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd")
#Column(name = "creation_date")
private Date creationDate;
//RELATIONSHIP
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
/* ======== GETTERS AND SETTERS ======== */
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPostContent() {
return postContent;
}
public void setPostContent(String postContent) {
this.postContent = postContent;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
This is the postController:
#RestController
public class PostController {
#Autowired
private PostService postService;
#PostMapping("/savePost")
public Post getPost(#Validated #RequestBody Post post) {
return postService.savePost(post);
}
#GetMapping("getPost/{id}")
public Post getPost(#PathVariable int id) {
return postService.getPost(id);
}
#PutMapping("/deletePost/{id}")
public void deletePost(int id) {
postService.deletePost(id);
}
}
This is the JSON I send in to add a post.
Request to: http://localhost:8080/savePost
JSON body:
{
"postContent": "some content",
"creationDate": "2022-07-31",
"title": "my title",
"user": 1
}
But in postMan i get this error:
{
"timestamp": "2022-08-02T10:40:11.794+00:00",
"status": 400,
"error": "Bad Request",
"path": "/savePost"
}
And in spring i get this error:
JSON parse error: Cannot construct instance of x.model.User (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1);
If i send in a JSON where I call the user for "user_id" or "uderId", then Im able to send the request, but then the foreign key turns into null
{
"creationDate": "2022-07-31",
"postContent": "some content",
"title": "my title",
"user_id": 1
}
what gets sent in:
{
"id": 2,
"postContent": "some content",
"title": "my title",
"creationDate": "2022-07-31",
"user": null
}
Does anyone knwo what im doing wrong?
I devleloping a REST API using spring boot
I have two entities with bidirectional OneToMany
Product class
public class Product {
private Long productId;
private String name;
private String description;
private List<ProductList> productList;
public Product() {
}
public Product(Long productId, String name, String description, List<ProductList> productList) {
this.productId = productId;
this.name = name;
this.description = description;
this.productList = productList;
}
public Long getProductId() {
return productId;
}
public void setProductId(Long productId) {
this.productId = productId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<ProductList> getProductList() {
return productList;
}
public void setProductList(List<ProductList> productList) {
this.productList = productList;
}
ProductList class
public class ProductList {
private Long productListId;
private String productListName;
private Product product;
public ProductList(Long productListId, String productListName, Product product) {
this.productListId = productListId;
this.productListName = productListName;
this.product = product;
}
public ProductList() {
}
public Long getProductListId() {
return productListId;
}
public void setProductListId(Long productListId) {
this.productListId = productListId;
}
public String getProductListName() {
return productListName;
}
public void setProductListName(String productListName) {
this.productListName = productListName;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
ProductEntity class:
#Entity
#Data
#Table(name = "PRODUCT")
public class ProductEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Integer productId;
#Column
private String name;
#Column
private String description;
#OneToMany(cascade = CascadeType.ALL, mappedBy="product", fetch =
FetchType.EAGER)
private List<ProductListEntity> productList;
// Getters,Setters,No-ArgCOnstructor, All-ArgConstructor
ProductListEntity class:
#Table
#Entity
#Data
public class ProductListEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long productListId;
private String productListName;
#ManyToOne(fetch = FetchType.EAGER, targetEntity = ProductEntity.class)
#JoinColumn(name = "FK_Product", referencedColumnName = "productId")
private ProductEntity product;
Service to save data:
public void addProduct(Product product) {
ProductEntity productEntity = new ProductEntity();
BeanUtils.copyProperties(product, productEntity);
productRepository.save(productEntity);
}
When i try to post I get this error:
"message": "JSON parse error: Cannot construct instance of
eteosf.hexagonal.domain.model.Product (although at least one Creator
exists): no String-argument constructor/factory method to deserialize
from String value ('string'); nested exception is
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot
construct instance of eteosf.hexagonal.domain.model.Product
(although at least one Creator exists): no String-argument
constructor/factory method to deserialize from String value
('string')\n at [Source: (PushbackInputStream); line: 9, column: 18]
(through reference chain:
eteosf.hexagonal.domain.model.Product["productList"]->java.util.ArrayList[0]->eteosf.hexagonal.domain.model.Product$ProductList["product"])",
"path": "/product"
JSON request body:
{
"productId": 0,
"name": "string",
"description": "string",
"productList": [
{
"productListId": 0,
"productListName": "string",
"product": "string"
}
]
}
in your json_request, inside of productList you are sending "product" as a "string". But Deserializer can not turn that string into a Product object. It has to be sent as object {}. You can leave that object empty - just not send it if all it does is point at itself.
You basically have made a mistake of confusing different principles - the whole bidirectional relationship is only to be applied at persistence level. Json requests are being sent at controller/view level and therefore you can't display the bidirectional nature in the same way. This is why you don't use Entities as controller params but use DTOs.
In your case just don't send the "product" field for the controller:
{
"productId": 0,
"name": "string",
"description": "string",
"productList": [
{
"productListId": 0,
"productListName": "string",
}
]
}
and just add it in the controller method right after receiving the parameter:
//the receiving controller method which got Parameter `ProductEntity product`
product.getProductList().forEach(productList -> productList.setProduct(product);
Like I said you shouldn't use entities in Controller method, and it should be a DTO class in order to avoid exactly this kind of issues
Below is my JSON file :
Input JSON:
{
"name": "Tamiliniyan",
"address": {
"street": "My street",
"city": "Texas"
}
}
Controller class:
#RestController
#RequestMapping(value = "/customer")
public class CustomerController {
#Autowired
private WelcomeService customerService;
#RequestMapping(method = RequestMethod.POST)
public void addCustomer(#RequestBody Customer customer) {
return customerService.addTranslation(customer);
}
}
POJO:
public class Customer
{
private Address address;
private String name;
public Address getAddress ()
{
return address;
}
public void setAddress (Address address)
{
this.address = address;
}
public String getName ()
{
return name;
}
public void setName (String name)
{
this.name = name;
}
}
public class Address
{
private String street;
private String city;
public String getStreet ()
{
return street;
}
public void setStreet (String street)
{
this.street = street;
}
public String getCity ()
{
return city;
}
public void setCity (String city)
{
this.city = city;
}
}
Now I have to dynamically add city or zipcode. How to do it? Basically Client system can pass any new additional JSON field with current structure (like city or zipcode). CustomerController class should able to parse it. What is better approach to handle dynamic JSON element in restful services?
In my opinion, the easiest and most performant way of handling JSON within Java using Spring when you don't know the final structure of your JSON is to use Map(s).
You could add something like this to your POJO:
public class Customer {
private Address address;
private String name;
private Map<String, ?> additionalFields;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, ?> getAdditionalFields() {
return additionalFields;
}
public void setAdditionalFields(Map<String, ?> additionalFields) {
this.additionalFields = additionalFields;
}
}
If you then post something like this:
{
"name": "Tamiliniyan",
"address": {
"street": "My street",
"city": "Texas"
},
"additionalFields": {
"nested1":{
"zip-code": "00055"
}
}
}
This is what you get when Spring processes it:
In order to retrieve data you could then use methods like:
customer.getAdditionalFields().containsKey("nested1")
customer.getAdditionalFields().get("nested1")
Another approach would be to add whatever fields you need to your Class and then ignore empty fields in you Jackson configuration