QUARKUS - MicroProfile REST Client: add a custom, not mapped field - java

I am following this article https://quarkus.io/guides/rest-client to build a REST Client to parse the output from the restcountries.eu service.
Here the class holding the model:
public class Country {
public String name;
public String alpha2Code;
public String capital;
public List<Currency> currencies;
public static class Currency {
public String code;
public String name;
public String symbol;
}
}
Now, suppose I would like to add a custom fields such as timestamp, to record the instant when this object has been created. I imagine, I would go ahead and add another field like below:
public class Country {
public String name;
public String alpha2Code;
public String capital;
public List<Currency> currencies;
public Instant timestamp; //<--------- added attribute
[....]
My question is: how do I tell the client to populate that field? Normally, I would have done it in the constructor. However, I could not find docs that explain this part.
Thanks for your help
Simone

You can actually do this in the default constructor. Frameworks like JSONB or Jackson expect POJOs to have a default constructor. They will call it when they create an instance of Country.
Use the #JsonbTransient or #JsonIgnore annotations to mark that attribute of your POJO as ignorable in order to avoid the unmarshaller complaining about attributes that cannot be found in the response.
#Data
public class Country {
private String name;
private String alpha2Code;
private String capital;
private List<Currency> currencies;
#JsonbTransient // if you're using JSONB (default in Quarkus)
#JsonIgnore // if you're using Jackson
private Instant timestamp;
public Country() {
this.timestamp = Instant.now();
}
PS The #Data annotation is something you should consider using. Encapsulation is not a bad thing but creating getters/setters is tedious. But Project Lombok certainly helps here.

Related

Create #ToLowerCase annotation to convert String value to lower case

We have SpringBoot application.
For our pojo's we want to create a custom #ToLowerCase annotation which converts the field variable value to lower case.
Eg:
#Data
Employee {
private String name;
#ToLowerCase
private String emailId;
private String gender;
private String phoneNumber;
}
So my custom #ToLowerCase annotation should convert emailId to lower case.
We want to use this annotation on all kind of Pojos, whether it is rest request pojo or JPA entity pojo.
I have gone through posts on many forums but didn't get any appropriate solution for same.
Is it possible to create such annotation in Spring Boot? If yes then how?
Kindly help
Thanks
Create a custom converter: ToLowerCaseConverter.
public class ToLowerCaseConverter extends StdConverter<String, String> {
#Override
public String convert(String value) {
if (value == null){
return null;
}
return value.toLowerCase();
}
}
After create a new annotation: ToLowerCase. It works for both incoming and outgoing Strings (#JsonDeserialize/#JsonSerialize).
#Retention(RetentionPolicy.RUNTIME)
#JacksonAnnotationsInside
#JsonSerialize(converter = ToLowerCaseConverter.class)
#JsonDeserialize(converter = ToLowerCaseConverter.class)
public #interface ToLowerCase {
}
Finally, your example will work as intended:
#Data
Employee {
#ToLowerCase
private String emailId;
}

How to fix " Failed to instantiate 'className' using constructor NO_CONSTRUCTOR with arguments" in immutable class

I use MongoDBRepository in spring boot, and when I save some object in database everything is ok. but when I find object by id spring does not allow do that.
I try to change VehicleRoutingProblemSolution type to Object type, but VehicleRoutingProblemSolution have other object field PickupService and it without default constructor to. And yes, this class has immutable... I can't create default constructors, what can I do?
import com.fasterxml.jackson.annotation.JsonProperty;
import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document(collection = "vrp_solutions")
public class VrpSolutionHolder {
// Specifies the solution id
#Id
#JsonProperty("id")
private String id;
// Specifies the solution id
#JsonProperty("solution")
private VehicleRoutingProblemSolution vehicleRoutingProblemSolution;
// Created at timestamp in millis
#JsonProperty("created_at")
private Long created_at = System.currentTimeMillis();
public VrpSolutionHolder(String id, VehicleRoutingProblemSolution vehicleRoutingProblemSolution) {
this.id = id;
this.vehicleRoutingProblemSolution = vehicleRoutingProblemSolution;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public VehicleRoutingProblemSolution getVehicleRoutingProblemSolution() {
return vehicleRoutingProblemSolution;
}
public void setVehicleRoutingProblemSolution(VehicleRoutingProblemSolution vehicleRoutingProblemSolution) {
this.vehicleRoutingProblemSolution = vehicleRoutingProblemSolution;
}
public Long getCreated_at() {
return created_at;
}
public void setCreated_at(Long created_at) {
this.created_at = created_at;
}
}
org.springframework.web.util.NestedServletException: Request
processing failed; nested exception is
org.springframework.data.mapping.model.MappingInstantiationException:
Failed to instantiate
com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution
using constructor NO_CONSTRUCTOR with arguments
I ran into the exact same problem. A persistent immutable class containing other class instances, throwing that aforementioned exception when retrieved by this repository method:
public interface ProjectCodeCacheRepository extends MongoRepository<CachedCode, String> {
public CachedCode findByCode(String code);
public List<CachedCode> findByClientId(UUID clientId);
}
...
List<CachedCode> cachedForClient = this.codeCacheRepo.`**findByClientId**`(clientId);
...
Following Erwin Smouts hints, this is nicely fixed by giving it a special constructor annotated org.springframework.data.annotation.PersistenceConstructor like so:
#Document(collection="cachedcodes")
public class CachedCode {
#PersistenceConstructor
public CachedCode(String code, UUID clientId, LocalDateTime expiration) {
this.code = code;
this.clientId = clientId;
this.expiration = expiration;
}
public CachedCode(String code, UUID clientId, long secondsExpiring) {
this.code = code;
this.clientId = clientId;
this.expiration = LocalDateTime.now().plusSeconds(secondsExpiring);
}
public UUID getClientId( ) {
return this.clientId;
}
public String getCode() {
return this.code;
}
public boolean hasExpired(LocalDateTime now) {
return (expiration.isBefore(now));
}
...
#Id
private final String code;
private final UUID clientId;
private final LocalDateTime expiration;
}
So, you should check if your VehicleRoutingProblemSolution has a) a constructor that matches the database fields (check in mongo client) and b) is annotated to be the one used by the driver (or whichever piece of Spring magic under the hood).
If your framework tool requires (visible) no-arg constructors (plus accompanying setters), and the class you have is required to stay as is, then you could roll your own, say, MutableVehicleRoutingProblemSolution where in the setters you could have :
this.vehicleRoutingProblemSolution = new VehicleRoutingProblemSolution(vehicleRoutingProblemSolution.getId(), newSolution);
Thus your MutableVehicleRoutingProblemSolution wraps around the existing VehicleRoutingProblemSolution.
Hacky smell to it, but it fits the requirements.
(Or you could try to find a tool that is able to use, not annotations on the contained fields, but annotations on constructor arguments.)
This is a problem where the corresponding class does not have a no-arg constructor like - I was facing an issue with java.io.File.
Solution:
In general - change the declaration to Object class and convert where we are using the class.
from
class MyClass{
File myfile;
}
to
class MyClass{
Object myFile;
}
For anyone using lombok, you need to remove the #Builder annotation on your class and use #Data instead, or follow the above solution to provide a specialized constructor
Oddly, I received this when I attempted to decorate a custom interface with ...
#Document(collection = "Person")
Example:
package test.barry.interfaces;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
#Document(collection = "Person")
public interface CustomRepository
{
void updatex(Query filterPredicate, UpdateDefinition updatePredicate);
}

Jackson: remove some values from json and keep some null values

I have a model like this:
public class Employee {
#JsonProperty("emplyee_id")
private Integer id;
#JsonProperty("emplyee_first_name")
private String firstName;
#JsonProperty("emplyee_last_name")
private String lastName;
#JsonProperty("emplyee_address")
private String address;
#JsonProperty("emplyee_age")
private Byte age;
#JsonProperty("emplyee_level")
private Byte level;
//getters and setters
}
now I need to create two JSONs using this (only) model.
the first one must like this for example:
{
"employee_id":101,
"employee_first_name":"Alex",
"employee_last_name":"Light",
"employee_age":null,
"employee_address":null
}
and the second one must like this for example:
{
"employee_id":101,
"employee_level":5
}
by the way, I already tested #JsonIgnore and #JsonInclude(JsonInclude.Include.NON_NULL).
the problem of the first one (as much as I know) is, those fields can't be included in other JSONs (for example if level get this annotation, it won't be included in the second JSON)
and the problem of the second one is, null values can't be included in JSON.
so can I keep null values and prevent some other property to be included in JSON without creating extra models? if the answer is yes, so how can I do it? if it's not I really appreciate if anyone gives me the best solution for this state.
thanks very much.
it could be useful for you using #JsonView annotation
public class Views {
public static class Public {
}
public static class Base {
}
}
public class Employee {
#JsonProperty("emplyee_id")
#JsonView({View.Public.class,View.Base.class})
private Integer id;
#JsonProperty("emplyee_first_name")
#JsonView(View.Public.class)
private String firstName;
#JsonProperty("emplyee_last_name")
#JsonView(View.Public.class)
private String lastName;
#JsonProperty("emplyee_address")
private String address;
#JsonProperty("emplyee_age")
private Byte age;
#JsonProperty("emplyee_level")
#JsonView(View.Base.class)
private Byte level;
//getters and setters
}
in your json response add #JsonView(Public/Base.class) it will return based on jsonview annotations
//requestmapping
#JsonView(View.Public.class)
public ResponseEntity<Employee> getEmployeeWithPublicView(){
//do something
}
response:
{
"employee_id":101,
"employee_first_name":"Alex",
"employee_last_name":"Light",
"employee_age":null,
"employee_address":null
}
for the second one
//requestmapping
#JsonView(View.Base.class)
public ResponseEntity<Employee> getEmployeeWithBaseView(){
//do something
}
response
{
"employee_id":101,
"employee_level":5
}

How to use JsonProperty annotation over a class reference

I have following class
public class Employee {
#JsonPropery("name")
private String name;
#JsonPropery("address")
private Address address;
// getter setters
}
And
public class Address {
#JsonPropery("street")
private String name;
#JsonPropery("location")
private String name;
// getter setters
}
Now while generating the JSON for this model, following error is obtained:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:251)
at org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:183)
Can you Please help out in what should be used in place of #JsonProperty here.
If the referenced class is removed , Proper JSON is obtained. Is there any specific annotation to be used for class reference ?

How to apply Single annotation on multiple variables?

I am rookie in Java Annotation and have been searching for applying single annotation on multiple variable simultaneously.
Code:
#Document(collection = "users")
public class User {
private ObjectId id;
#NotNull
private String email;
private String imageURL;
private String authToken;
private Date createdDate;
private Date updateDate;
private boolean isActivated;
private int credits;
.....getter/Setter Method
I want to apply #NotNull property on email, imageURL and authToken too. I can do it by writing #NotNull to each variable but not preferring. How to do it?
#NotNull annotation can be applied at element not at group of elements.
JavaDoc: The annotated element must not be null. Accepts any type.
If you really want to get away with boiler plate code, you can use frameworks like Lombok which can help you to certain extent.
Link : http://projectlombok.org/features/Data.html
OR you can use reflection to validate all the method.
for (Field f : obj.getClass().getDeclaredFields()) {
f.setAccessible(true); // optional
if (f.get(obj) == null) {
f.set(obj, getDefaultValueForType(f.getType()));
// OR throw error
}
}
Java does not support multiple annotation of this type. But you can write something like this
Create a class with annotated field.
Create setters and getters to access the field.
Create all your name,email field as instance of this class.
This way fields will implicitly annotated as NotNull.
public class NotNullString {
#NotNull
String str;
public void set(String str)
{
this.str = str;
}
public String get()
{
return this.str;
}
}
NotNullString name;
NotNullString email;

Categories