After going through similar questions, I am not still able to solve this problem. I am using Jackson to serialize and deserialize class that does not have matching getters/setters method.
public class Products implements Serializable {
private static final long serialVersionUID = 1L;
#JsonProperty
private final Map<Product, String> prices;
public Products() {
this.prices = new HashMap<>();
}
public void addPrice(final Product product, final String price) {
prices.put(product, price);
}
}
Product Class:
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
#JsonProperty
private final String name;
#JsonProperty
private final String type;
public Product(final String price, final String type) {
this.name = price;
this.type = type;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public static void main(String[] args) {
Products products = new Products();
products.addPrice(new Product("apple", "fruit"), "16");
JsonDataConverter converter = new JsonDataConverter();
String json = converter.toData(products);
System.out.println(json);
Products deserializedProducts = converter.fromData(json, Products.class);
}
}
JsonDataConverter I am using is from AWS Flow Framework: http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/simpleworkflow/flow/JsonDataConverter.html
Exception I am getting at deserialization step is: Can not find a (Map) Key deserializer for type [simple type, class mypackage.Product] when mapping key "null".
I am not able to understand this since my prices map does not contain any null values. Strange thing is, It's working fine if Product has only 1 member (just name) field.
Any idea what's is going wrong?
Thanks
Can u try modify the constructor of product and products classes to have JsonCreator annotation and annotate each param with JsonProperty.
Example:
#JsonCreator
public Product(#JsonProperty("price") final String price, #JsonProperty("type") final String type) {
this.name = price; this.type = type;
}
Related
I have a model class "Person" that have four attribute:
private final static String id;
private final static String name;
private final static String lastName;
private final static String age;
Now I must create a event class which contains the list of person ID(so I suppose a method that returns a list) to be synchronized(all those existing in the system).
How could I do it as cleanly as possible? Thanks a lot.
My event class:
public class MyEventSync extends AbstractEvent{
private final PersonModel PersonModel;
public MyEventSync(PersonModel personModel)
{
this.personModel = personModel;
}
public List<String> getPersonsById()
{
List<String> personModelListId = new ArrayList<>();
personModelListId.add(personModel.getId());
return personModelListId;
}
or another method:
public List<PersonModel> getPerson()
{
List<PersonModel> personList = new ArrayList<>();
personList.add(personModel);
return personList;
}
I using RestController update data to db but I have problem. When i update value, if value from my update is null , it allways update data to db is null. I dont't want it. I want if 1 field with value is null from my request, i don't want update it.
This bellow my code :
Controller:
RestController
#RequestMapping("/api/products")
#Api(value = "ProductControllerApi",produces = MediaType.APPLICATION_JSON_VALUE)
public class ProductController {
#Autowired
private ProductService productService;
#PatchMapping("/{id}")
public ResponseEntity<ProductResDto> updateProduct(#RequestBody ProductReqDto productReqDto, #PathVariable String id) {
return ResponseEntity.ok(productService.updateProduct(product,id));
}
ProductReqDto:
public class ProductReqDto {
private String name;
private String type;
private String category;
private String description;
private Double prince;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Double getPrince() {
return prince;
}
public void setPrince(Double prince) {
this.prince = prince;
}
}
ProductResDto:
public class ProductResDto {
private String name;
private String type;
private String category;
private Double prince;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Double getPrince() {
return prince;
}
public void setPrince(Double prince) {
this.prince = prince;
}
}
MappingDto:
private ProductDto convertToProductDto(ProductReq product) {
return modelMapper.map(product, ProductResDto.class);
}
How to i handle method convertToProductDto only mapping with value not null. Because if , mapping one field : example : product_name = null , it insert to db null. I want if field ProductReq have value, it mapping and keep other different field in database(not set it null if not contain value from ProductReq) .
Example:
**ReqProductDto.class**
private String name;
private String type;
private String category;
private String description;
private Double prince;
but if user only update two field:
private String name;
private String type;
I want spring update field name, and field type user input and keep category,description,prince in my database. In my case, if user update two field: name, and field type,spring update it but spring set category,description,prince is null in my database. I don't want it.
Please help me, thanks.
You've tagged this as spring-boot, so I'm assuming you might be using controllers and validating their parameters. If that is the case, just do
import javax.validation.constraints.NotNull;
public class ProductReqDto {
#NotNull
private String name;
#NotNull
private String type;
#NotNull
private String category;
#NotNull
private String description;
#NotNull
private Double prince;
...
}
and use #Valid for your controllers like this
#PatchMapping("/{id}")
public ResponseEntity<ProductResDto> updateProduct(#RequestBody #Valid ProductReqDto productReqDto, #PathVariable String id) {
return ResponseEntity.ok(productService.updateProduct(product,id));
}
Then your object will be validated on instantiation.
What you want is mainly used for PATCH mapping.
IN a PUT mapping, all fields of an object need to override, but in a PATCH mapping only the fields which are provided needs to be overridden, others need not be changed.
So,
for an existing record,
employee{ employeeId = "A2RTD", empName = "satish", "country": "India"}
And, now one non-mandatory field mobileNo needs to be updated along with the country
DTO request will contain all field other than id, but only country & mobile no will not be null
In this scenario, we can use BeanUtils which is part of spring package
import org.springframework.beans.BeanUtils;
public static Object getDtoMapping(Object source, Object destination) {
BeanUtils.copyProperties(source, destination, getNullFieldNames(source));
return destination;
}
public static String[] getNullFieldNames(Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> fieldNames = new HashSet<>();
for (PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null)
fieldNames.add(pd.getName());
}
String[] result = new String[fieldNames.size()];
return fieldNames.toArray(result);
}
Ths function "getNullFieldNames" will return fieldNames which have value null. So, those fields will not be mapped, as per 3rd optional paramter in BeanUtils
And, you need to pass
// PATCH
EmployeeDao emp = findById(empCode);
emp = (EmployeeDao) getDtoMapping(empUpdateDto, emp);
Here, in BeanUtil copyProperties, 3rd param is optional. If you give it works for PATCH mapping, if you don't give it behaves as PUT mapping.
Since, for PUT mapping, ignoring null as same as not ignoring.
You can use the same in POST, PUT mapping also.
// POST MAPPING
EmployeeDao emp = (EmployeeDao) getDtoMapping(empCreateDto, new Employee());
I'm working with Spring Boot 2.0RC2 and in the documentation I read you can return a projection of an entity instead of the entity as a whole when calling the Repository. This is working fine in case I use a String in my Entity but not when I use an embedded value objects.
Let's say I have the Product entity:
#Entity
#Table(name = "t_product")
public class Product extends BaseEntity {
#Column(nullable = false)
private String name;
private Product() {}
private Product(final String name) {
this.name = name;
}
public static Result<Product> create(#NonNull final String name) {
return Result.ok(new Product(name));
}
public String getName() {
return name;
}
public void setName(#NonNull final String name) {
this.name = name;
}
}
The BaseEntity simply holds the id, created and updated attributes.
I have my projection interface called ProductSummary:
interface ProductSummary {
String getName();
Long getNameLength();
}
And in my ProductRepository I have the following method that returns the ProductSummary:
public interface ProductRepository extends JpaRepository<Product, Long> {
#Query(value = "SELECT p.name as name, LENGTH(p.name) as nameLength FROM Product p WHERE p.id = :id")
ProductSummary findSummaryById(#Param("id") Long id);
}
This works perfectly fine. Now let's say I am doing DDD and instead of using a String to represent the name attribute in the Product entity, I want to use a value object called Name:
#Embeddable
public class Name implements Serializable {
public static final int MAX_NAME_LENGTH = 100;
#Column(nullable = false, length = Name.MAX_NAME_LENGTH)
private String value;
private Name() {}
private Name(final String value) {
this.value = value;
}
public static Result<Name> create(#NonNull final String name) {
if (name.isEmpty()) {
return Result.fail("Name cannot be empty");
}
if (name.length() > MAX_NAME_LENGTH) {
return Result.fail("Name cannot be longer than " + MAX_NAME_LENGTH + " characters");
}
return Result.ok(new Name(name));
}
public String getValue() {
return value;
}
}
I change my Product entity to:
#Entity
#Table(name = "t_product")
public class Product extends BaseEntity {
#Embedded
private Name name;
private Product() {}
private Product(final Name name) {
this.name = name;
}
public static Result<Product> create(#NonNull final Name name) {
return Result.ok(new Product(name));
}
public Name getName() {
return name;
}
public void setName(final Name name) {
this.name = name;
}
}
And in the ProductSummary I change the return type from String to Name.
When I run that I always get the exception:
Caused by: java.lang.IllegalAccessError: tried to access class com.acme.core.product.ProductSummary from class com.sun.proxy.$Proxy112
Can I make this work or am I missing some restriction which doesn't allow this?
If you wish to get the complete Name field(not a particular field in Name class), then you need to create another interface like ProductSummary.
interface ProductSummary {
NameSummary getName();
interface NameSummary {
String getValue();
}
}
No need to change anything in your repository.
It is quite clearly documented here
And make sure your interfaces and the methods are public.
I'm using DynamoDB and I would like to store the enum's String values instead of the enum itself.
For instance, I have this enum:
public enum Source {
BREACH("breach"),
LEAKAGE("leakage");
private final String desc;
Source(String desc) { this.desc = desc; }
public String desc() { return desc; }
}
...and this "entity":
#DynamoDBTable(tableName = "Alerts")
public final class Alert implements Serializable {
private static final long serialVersionUID = 4012517315640518044L;
#DynamoDBHashKey(attributeName = "AlertId") // Partition Key or Hash Attribute
private String alertId;
#DynamoDBTypeConvertedEnum
#DynamoDBAttribute(attributeName = "Source")
private Source type;
// Constructor(s), Getter(s), Setter(s), ToString, etc...
}
With the #DynamoDBTypeConvertedEnum annotation, the value that gets saved is BREACH, but I want breach.
{
"AlertId": { "S": "a083168d-cb23-4ec8-ab80-a1c16955c4b8" },
"Source": { "S": "BREACH" },
...
"CreatedAt": { "S": "2017-05-03T14:07:36.395Z" }
}
Any clues? I did try "converters" (not fully, I couldn't make it work though) but I think I have to end up doing one for each enum type since they are all different.
You can code the Alert class like this i.e. define the attribute as String and design the getter and setter to send/receive enum object (i.e. Source).
Alert class:-
#DynamoDBTable(tableName = "Alerts")
public final class Alert implements Serializable {
private static final long serialVersionUID = 4012517315640518044L;
private String alertId;
private String type;
#DynamoDBHashKey(attributeName = "AlertId")
public String getAlertId() {
return alertId;
}
#DynamoDBAttribute(attributeName = "Source")
public Source getType() {
if (type != null)
return Source.valueOf(type);
else
return null;
}
public void setAlertId(String alertId) {
this.alertId = alertId;
}
public void setType(Source type) {
this.type = type.desc();
}
}
Create Alert:-
Stores the value as expected on database table. The get item from DynamoDB table also works fine.
public Boolean createAlert(String alertId, Source source) {
DynamoDBMapper dynamoDBMapper = new DynamoDBMapper(dynamoDBClient);
Alert alert = new Alert();
alert.setAlertId(alertId);
alert.setType(source);
dynamoDBMapper.save(alert);
return true;
}
Override toString() this should work.
public enum Source {
BREACH("breach"),
LEAKAGE("leakage");
private final String desc;
Source(String desc) { this.desc = desc; }
public String desc() { return desc; }
#Override
public String toString() { return desc; }
}
In my project, I'm facing to a problem of JSON serialization. I have made a quick simple example to illustrate it:
I have created an object Session which contains a hashMap.
public class session {
private Map<String, Object> mapKeyValue = new HashMap<>();
public Map<String, Object> getMapKeyValue() {
return mapKeyValue;
}
public void setMapKeyValue(Map<String, Object> mapKeyValue) {
this.mapKeyValue = mapKeyValue;
}
}
This map can contains various object. I only know the type of these objects at runtime. Example :
public class Dog {
private final String race;
private final String name;
public Dog(String race, String name) {
this.name = name;
this.race = race;
}
public String getName() {
return name;
}
public String getRace() {
return race;
}
}
or
public class Insect {
private final int nbPates;
private final String name;
public Insect(int nbPates, String name) {
this.nbPates = nbPates;
this.name = name;
}
public int getNbPates() {
return nbPates;
}
public String getName() {
return name;
}
}
I want to save my object session to a database to be able to restore it later.
My first idea was to serialize my session object in json and then to save to to database.
public static void main(String[] args) {
final session session = new session();
session.add("Dog", new Dog("labrador", "milou"));
session.add("Insect", new Insect(4, "cafar"));
String output = serializeJSON(session);
final session session2 = (session) deserializeJSON(output, session.class);
Dog dog = (Dog)session2.get("Dog");
}
It works well. When i deserialize it, there is no error, but as a result, the map contained in the deserialized Session doesn't contain the original type of object but contains hashMap$Node objects.
I guess the deserialization can not guess the types of objects from the string.
Is there a workAround or any other strategy to save my session to database and to be able to restore it as it was previously ?