My goal is to create class level annotations, but I keep getting this error. For some reason, reflection isn't working when I use getClass to get the information of the annotation. I tried using aspects, but I'm not sure how to make spring boot aspect annotations class level.
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class java.lang.Class.
Below are the classes I used
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Target(ElementType.TYPE)
public #interface ValidateInput {
short minLength();
short maxLength();
String regex() default "";
}
public abstract class SimpleValidation {
private String text;
private ValidateInput info = this.getClass().getAnnotation(ValidateInput.class);
protected SimpleValidation(String text) {
this.text = text;
}
}
#Value
#EqualsAndHashCode(callSuper = true)
#ValidateInput(maxLength = 50, minLength = 5, regex = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+#[a-zA-Z0-9.-]+$")
public class Email extends SimpleValidation {
String email;
public Email(String email) {
super(email);
this.email = email;
}
}
I think you are trying to recreate the Bean validation behaviour which is already something standard, try to use it, as it will be easier than creating a heavy hierarchy of classes for achieving that.
Spring will be able to verify your validation automatically (After deserializing a JSON body, before inserting in DB..)
Related
I am using spring data neo4j 6.1.3 and following is my use case code snippets
Domain Entity
#Data
#Node("DATspace")
public class DatSpace {
#Id #GeneratedValue
private Long neoId;
#Property("SUPtitle")
private String title;
private String SUPid;
}
Test class
#SpringBootTest
#EnableNeo4jRepositories(basePackages = "com.rahal.marvel")
public class ProjectionTest {
#Autowired
private Neo4jTemplate neo4jTemplate;
interface DATspaceProjection {
String getTitle();
String getSUPid();
}
#Test
public void test_projection(){
DatSpace d = neo4jTemplate.findOne("MATCH (s:DATspace {SUPid: $id}) RETURN s", Collections.singletonMap("id", "SPC_ML7"), DatSpace.class).get();
d.setTitle("title modified");
d.setSUPid("SUPid modified");
DATspaceProjection p = neo4jTemplate.saveAs(d, DATspaceProjection.class);
}
}
Ideally above saveAs function should modify both DATspace.SUPtitle and DATspace.SUPid. However it only modify SUPid but not SUPtitle. I presume it is due to property mapping (#Property) . Is this a bug or are there any workaround?
The provided #Property annotation does only have an impact on the annotated property (title) itself.
There is no knowledge right now that goes from the getTitle() method in the projection to the annotated title field in the domain class.
To be safe when modifying this use the explicit property name:
interface DATspaceProjection {
String getSUPtitle();
String getSUPid();
}
I created an issue for improvement https://github.com/spring-projects/spring-data-neo4j/issues/2371
I've run into a situation where I need to validate a field inside an object conditionally. More specifically, I have one class PhoneType which contains two fields
#Getter
#Setter
public class PhoneType {
#JsonProperty("#CountryCode")
private String countryCode;
#JsonProperty("#Number")
private String number;
}
The class PhoneType is used in three places,
#Getter
#Setter
class PersonContact {
#JsonProperty("Mobile")
private PhoneType mobile;
#JsonProperty("WorkPhone")
private PhoneType workPhone;
#JsonProperty("OfficeFax")
private PhoneType officeFax;
}
However, with mobile, there should be an additional validation rule applied to the number field. The number must be a number with length of 10.
I have two possible solutions in mind:
Create a custom annotation to validate number for mobile
Validate number using Jackson's StdConverter
Here are the implementation of both solutions
public class ContactConverter extends StdConverter<PersonContact, PersonContact> {
#SneakyThrows
#Override
public PersonContact convert(PersonContact personContact) {
boolean validMobilePhone = Pattern.compile("\\d{10}")
.matcher(relatedPersonContact.getMobileNumber())
.matches();
if (BooleanUtils.isFalse(validMobilePhone)) {
var errorMessage = String.format(INVALID_MOBILE_NUMBER, personContact.getMobileNumber());
throw new JsonParseException(null, errorMessage);
}
return personContact;
}
}
Converter is used like this
#Getter
#Setter
#JsonDeserialize(converter = ContactConverter.class)
public class PersonContact {
#JsonProperty("#Email")
private String email;
#JsonProperty("WorkPhone")
private PhoneType workPhone;
#JsonProperty("Mobile")
private PhoneType mobile;
}
This is the code for custom annotation, however it's not working
#Retention(RUNTIME)
#Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Constraint(validatedBy = MobilePhoneNumberValidator.class)
#interface Phone {
String format() default "";
String message() default "Invalid phone number";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
static class MobilePhoneNumberValidator implements ConstraintValidator<Phone, PhoneType> {
#Override
public void initialize(Phone constraintAnnotation) {
System.out.println("test");
}
#Override
public boolean isValid(PhoneType phoneType, ConstraintValidatorContext context) {
System.out.println("test again");
return false;
}
}
and this is how I use it
#Phone(format = "\\d{10}")
#JsonProperty("Mobile")
private PhoneType mobile;
However, the code inside the Validator is not executed.
I wonder if there is anything wrong with the custom annotation. This is SpringBoot 2.3.0, I can't think of any other reason why the custom annotation is not working.
Please help if you know there is a legit way handling dynamic annotation in Java, or you know why the above code isn't working, or you know a legit way of validating object's field just by name.
EDIT 1:
It seems like due to my poor way of explanation, there is misunderstanding.
https://www.baeldung.com/javax-validation-groups , this doesn't work in this case, the validation is applied only with the declaration of the PhoneType property in other classes (PersonContact)
I have two possible solutions, custom annotation and Jackson's converter.
I have successfully applied the converter but couldn't make the custom annotation work.
My custom annotation should be run after #JsonProperty, because it needs to have the field PhoneType mobile number to be deserialized.
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;
}
in my project I am using Swagger to document REST API. I have simple value object which I want to document.
public class MyClass {
#JsonValue
private String myField;
public String getMyField() {
return myField;
}
}
Unfortunately, when I am adding swagger annotations and then see created documentation, there is no information about this VO.
Class with swagger annotations below:
#ApiModel(value = "MyClass ", description = "represents my class")
public class MyClass {
#JsonValue
#ApiModelProperty(value = "name", dataType = "String", example = "my field")
private String myField;
public String getMyField() {
return myField;
}
}
Wanted to check this issue I temporally removed #JsonValue annotation and documentation was created properly, annotation #ApiModelProperty worked.
I cannot remove #JsonValue annotation permanently.
Does anybody know the solution how can I force those two tools to cooperate?
I have a scenario where a property has a different validation applied depending on end-point. This is done via "groups" (code example below & reference to dropwizard doc: http://www.dropwizard.io/0.9.3/docs/manual/validation.html#validated). I implemented the DropWizard validations but the Swagger does not recognizing the "groups" and therefore doesn't provided correct model description. In addition Swagger doesn't seem to have any annotations that would accomplish the scenario. Can you please help? Thank you.
public interface Version1FirstName { }
public interface Version2FirstName { }
class Person {
#NotEmpty(groups = Version1FirstName .class)
#Length(max = 5, groups = Version2FirstName .class)
private firstName;
private lastName;
}
#POST
#Path("/v1")
public void addName(#Validated(Version1FirstName.class) Person person) {}
#POST
#Path("/v2")
public void retriveName(#Validated({Version2FirstName.class,
Version1FirstName.class}) Person person) {}