Desrialzing JSON in spring boot, where a field is the combination of two fields - spring

I have a controller with the following signiture:
public HttpEntity<RepresentationModel> confirmRegistration(#Valid #RequestBody RegistrationRequest request{}
the RegistrationRequest Json looks like this
{
//other fields
"countryCode":"44",
"mobileNumber": "07545878096"
}
I am trying to write a custom deserializer for this json
My mobileNumber class looks like this:
#Getter
#Setter
#EqualsAndHashCode
#ToString
#AllArgsConstructor
public class MobileNumber {
#JsonProperty("mobilePhoneNumber")
#JsonAlias("mobileNumber")
String number;
#JsonProperty(value = "countryCode", defaultValue = "44")
String countryCode;
}
and a request object like so:
public class RegistrationRequest {
//other fields
#JsonDeserialize(using = MobileNumberDeserializer.class)
#MobileNumberValidator
private final MobileNumber mobilePhoneNumber;
}
where the MobileNumberDeserializer looks like this:
public class ContactNumberDeserializer extends StdDeserializer<MobileNumber> {
private static final long serialVersionUID = 1L;
protected ContactNumberDeserializer() {
super(MobileNumber.class);
}
#Override
public MobileNumber deserialize(JsonParser jsonParser, DeserializationContext ctxt)
throws IOException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
String mobileNumber = "";
if (node.has("mobilePhoneNumber")) {
mobileNumber = node.get("mobilePhoneNumber").asText();
} else if (node.has("phoneNumber")) {
mobileNumber = node.get("phoneNumber").asText();
} else if (node.has("mobileNumber")) {
mobileNumber = node.get("mobileNumber").asText();
}
String countryCode = node.get("countryCode").asText();
return new MobileNumber(mobileNumber, countryCode);
}
when the ContactNumberDeserializer is invoked by the controller,jsonParser.getCodec().readTree(jsonParser); it's just the mobilePhoneNumber node and cant access countryCode.

Quick check if ContactNumber and MobileNumber are the same classes.
Ideally it should be
public class ContactNumberDeserializer extends StdDeserializer<MobileNumber {
...
}

In your MobileNumber class:
#Getter
#Setter
#EqualsAndHashCode
#ToString
#AllArgsConstructor
public class MobileNumber {
#JsonProperty("mobilePhoneNumber")
#JsonAlias("mobileNumber")
String number;
#JsonProperty("countryCode")
String countryCode = "44";
}
Update JsonProperty annotation like above for countryCode. Hope it helps!

You don't need to write ContactNumberDeserializer. If you wrote your class MobileNumber it would simply work.

Related

How to remove empty object in which all the fields are null and those fields are removed by using " #JsonInclude(Include.NON_NULL) "?

I am using this #JsonInclude(Include.NON_NULL) to remove null fields from the Objects. It is working as expected. Let's see with an example .
If I have an object like this -
User{id=null,name=null}
(user object which has all of its fields as null value).
In the response it is coming like ----
{
user:{}
}
This is the thing I need to remove. Either I should assign null or remove the entire property.
Thanks for the help !.
To meet your requirement we need to use #JsonSerialize annotation with User field. Here is the working code.
#Getter
#Setter
public class Demo implements Serializable{
#JsonSerialize(using = CustomJsonSerializer.class)
private User user;
}
#Getter
#JsonInclude(JsonInclude.Include.NON_NULL)
public class User implements Serializable{
private Integer id;
private String name;
public User() {
}
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
}
public class CustomJsonSerializer extends JsonSerializer<User> {
#Override
public void serialize(User user, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (user.getId() == null && user.getName() == null) {
jsonGenerator.writeNull();
} else {
jsonGenerator.writeStartObject();
jsonGenerator.writeNumberField("id", user.getId());
jsonGenerator.writeStringField("name", user.getName());
jsonGenerator.writeEndObject();
}
}
}
if User{id=null,name=null} then response will be
{
"user":null
}
Reference - https://www.sghill.net/2012/how-do-i-write-a-jackson-json-serializer-deserializer/

Spring Boot Lombok API not serializing

I have a simple Spring Boot controller with a simple Object, which is annotated with Lombok, when I tried to post data to the controller the object to not serializing.
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
#EqualsAndHashCode(callSuper = true)
#ToString(callSuper = true)
public class Employee extends BaseDomain {
private String firstName;
private String middleName;
private String lastName;
private String email;
private String mobileNo;
#PostMapping
public Employee saveEmployee(Employee employee) {
log.debug("Employee save {}", employee);
return employeeService.saveOrUpdateEmployee(employee);
}
}
#PostMapping
public Employee saveEmployee(#Requestbody Employee employee) {
log.debug("Employee save {}", employee);
return employeeService.saveOrUpdateEmployee(employee);
}
#Requestbody is missing

How to extend Panache with static method?

Hello. I want to write a static method in class with #MapperdSupperclass. such as findByName. so the child object can use method to query itself.
But I got error. It will query BaseUpdatableEntity not the childObject.The code like this.
#MappedSuperclass
#Slf4j
#ToString
public abstract class BaseUpdatableEntity extends BaseEntity {
#Getter
protected LocalDateTime created;
#Getter
protected LocalDateTime updated;
#Getter
protected String createdBy;
#Getter
protected String updatedBy;
#Version
#Setter
#Getter
protected Long lockVersion = 1L;
#Override
protected void prePersist() {
super.prePersist();
created = LocalDateTime.now();
createdBy = MDCScope.getUsername();
}
#PreUpdate
protected void preUpdate() {
updated = LocalDateTime.now();
updatedBy = MDCScope.getUsername();
}
#Override
public Object clone() throws CloneNotSupportedException {
BaseUpdatableEntity updatableEntity = (BaseUpdatableEntity) super.clone();
updatableEntity.setLockVersion(1L);
return updatableEntity;
}
public BaseUpdatableEntity getByName(String name) {
return find("name", name).<BaseUpdatableEntity>singleResultOptional().orElse(null);
}
}
so I read the PanacheEntityBase code. I find the static method with #GenerateBridge
But I don't konw how to archive it with myself?

Spring Boot request body validation

I am working on Spring REST API and have following controller:
#RestController
#RequestMapping(
value = "/api/Test",
produces = "application/json"
)
public class MyController {
#RequestMapping(method = RequestMethod.POST)
public Response serviceRequest(#Valid #RequestBody ServiceRequest myServiceRequest) {
....
}
}
ServiceRequest has following structure:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class ServiceRequest {
#NotBlank
private LocalDate fromDate;
#NotBlank
private LocalDate toDate;
}
My task is to introduce validation based on combination of fromDate and toDate field's values: if time period between them is longer that 1 week then validation should fail.
What is the best way to archive this please?
You may create a custom constraint validator that will validate the required conditions before processing the request.
DatesDifference.java
#Constraint(validatedBy = DatesDifferenceValidator.class)
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface DatesDifference {
String message() default "Dates difference is more than a week";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
DatesDifferenceValidator.java
#Component
public class DatesDifferenceValidator implements ConstraintValidator<DatesDifference, ServiceRequest> {
#Override
public boolean isValid(ServiceRequest serviceRequest, ConstraintValidatorContext context) {
System.out.println("validation...");
if (!(serviceRequest instanceof ServiceRequest)) {
throw new IllegalArgumentException("#DatesDifference only applies to ServiceRequest");
}
LocalDate from = serviceRequest.getFromDate();
LocalDate to = serviceRequest.getToDate();
long daysBetween = ChronoUnit.DAYS.between(from, to);
System.out.println("daysBetween "+daysBetween);
return daysBetween <= 7;
}
}
ServiceRequest.java
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#DatesDifference
public class ServiceRequest {
#NotBlank
private LocalDate fromDate;
#NotBlank
private LocalDate toDate;
}

Spring Data REST and custom entity lookup (Provided id of the wrong type)

I have a model that looks something like this:
#Entity
public class MyModel {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(unique = true, nullable = false)
#RestResource(exported = false)
private int pk;
#Column(unique = true, nullable = false)
private String uuid = UUID.randomUUID().toString();
#Column(nullable = false)
private String title;
public int getPk() {
return pk;
}
public void setPk(int pk) {
this.pk = pk;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
As you can see I have an auto-incrementing PK as my ID for the model, but also a random UUID. I want to use the PK in the database as the primary key, but want to use the UUID as a public facing ID. (To be used in URLs etc.)
My repository looks like this:
#RepositoryRestResource(collectionResourceRel = "my-model", path = "my-model")
public interface MyModelRepository extends CrudRepository<MyModel, String> {
#RestResource(exported = false)
MyModel findByUuid(#Param("uuid") String id);
}
As you can see I've set the repository to use a String as the ID.
Finally I set the entity lookup in a config file like this:
#Component
public class RepositoryEntityLookupConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.withEntityLookup().forRepository(MyModelRepository.class, MyModel::getUuid, MyModelRepository::findByUuid);
}
}
This works perfectly well for GET and POST requests, but for some reason I get an error returned on PUT and DELETE methods.
o.s.d.r.w.RepositoryRestExceptionHandler : Provided id of the wrong type for class MyModel. Expected: class java.lang.Integer, got class java.lang.String
Anyone know what might be causing this? I don't understand why it's expecting an Integer.
I may be doing something stupid as I'm quite new to the framework.
Thanks for any help.
The identifier of your domain object is obviously of type int. That means, your repository needs to be declared as extends CrudRepository<MyModel, Integer>.

Resources