How can I disable JSR-303 annotation processing in springdoc for specific fields?
I have the following request class MyRequestTO where the field name is actually optional. The #NotBlank annotation is only applied to the unwrapped JsonNullable. This means the user is allowed to omit the field when sending MyRequestTO but if set it must not be blank. However the open api doc marks the name field as required. Changing the #Schema annotation to #Schema(type = "string", required = false) does not help.
I want to avoid a solution where I have to write my own annotation and make use of org.springdoc.core.customizers.OpenApiCustomiser. The desired solution should also work for other types like JsonNullable<Boolean> annotated with #NotNull.
public class MyRequestTO {
#Schema(type = "string")
#NotBlank
private JsonNullable<String> name = JsonNullable.undefined();
public JsonNullable<String> getName() {
return name;
}
public void setName(JsonNullable<String> name) {
this.name = name;
}
}
Relevant dependencies
implementation "org.openapitools:jackson-databind-nullable:0.2.1"
implementation "org.springdoc:springdoc-openapi-ui:1.5.5"
This is not working as #NotBlank allow null values
The #NotNull class isValid() method is called after the #NotBlank class isValid(), hence forbidding null values.
So you can try with #Pattern with validation of not black string as follows :
#Pattern(regexp = "/^$|\\s+/")
String name
This will allows not null values but not allow empty string
#crizzis solution from the comments to my question works as expected. Fields are no longer marked as required but if supplied have to conform to the annotation constraints.
The JSR-303 (e.g. #NotBlank or #NotNull) annotation belongs in front of the type parameter:
private JsonNullable<#NotBlank String> name = JsonNullable.undefined();
private JsonNullable<#NotNull Boolean> enabled = JsonNullable.undefined();
Then the resulting openAPI docs will mark the fields as "required" : false.
Related
Let's say I have the following objects
public class ClassA {
private String fieldA;
private String fieldB;
}
public class ClassB {
private String fieldA;
private String fieldBWithDifferentName;
private String fieldC;
}
I want to force explicit mappings for all fields in ClassB and force a compile error when a field is not mapped.
I know I can use #Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR) for fieldC to force #Mapping(target = "id", ignore = true) so my mapper would look like this
#Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface Mapper {
#Mapping(target = "fieldBWithDifferentName", source = "fieldB")
#Mapping(target = "fieldC", ignore = true)
ClassB toClassB(final ClassA classA);
}
However, this mapper will automatically map fieldA, because it exists in both classes. And even though I do want to map fieldA, I want the developer to have to do it explicitly and have mapstruct throw an error if it has to resort to the automatic mapping functionality.
Basically, I want to force a #Mapping for every field in the target. Is this possible?
Disabling implicit mapping is possible via #BeanMapping(ignoreByDefault = true) but this annotation must be set on each method. Look at the MapStruct reference guide in § 3.1. Basic mappings :
By means of the #BeanMapping(ignoreByDefault = true) the default
behavior will be explicit mapping, meaning that all mappings have to
be specified by means of the #Mapping and no warnings will be issued
on missing target properties. This allows to ignore all fields, except
the ones that are explicitly defined through #Mapping.
This is definitely not a duplicate of Only using #JsonIgnore during serialization, but not deserialization. The problem is the same but in the context of Immutables.
When a model(DTO/DAO) is decorated as an Immutable, I am not able to selectively #JsonIgnore one of the properties during serialization. Suppose that we have a UserDto which is defined as an Immutable as follow
#Value.Immutable
#Value.Style(defaults = #Value.Immutable(copy = false), init = "set*")
#JsonSerialize(as = ImmutableUserDto.class)
#JsonDeserialize(builder = ImmutableUserDto.Builder.class)
public abstract class UserDto {
#JsonProperty("id")
#Value.Default
public int getId() {
return 0;
}
#JsonProperty("username")
public abstract String getUsername();
#JsonProperty("email")
public abstract String getEmail();
#JsonProperty("password")
public abstract String getPassword();
}
I believe it is fair to expect that during serialization we would want to ignore the password from the response of the service.
Without using Immutables if we were working with a simple class, then there are many ways to accomplish this. For example - annotate only the getter with #JsonIgnore. Or if possible define a different accessor method (something that doesn't have the get prefix) and only define the regular setter method... and so on.
If I try the same on the Immutables accessor method for the password as shown below:
#Value.Immutable
#Value.Style(defaults = #Value.Immutable(copy = false), init = "set*")
#JsonSersonIgnoreialize(as = ImmutableUserDto.class)
#JsonDeserialize(builder = ImmutableUserDto.Builder.class)
public abstract class UserDto {
....
#JsonProperty("password")
#JsonIgnore
public abstract String getPassword();
}
then, the generated ImmutableUserDto adds the #JsonIgnore on both the getter and setter as shown below.
#Generated(from = "UserDto", generator = "Immutables")
#SuppressWarnings({"all"})
#ParametersAreNonnullByDefault
#javax.annotation.Generated("org.immutables.processor.ProxyProcessor")
#Immutable
#CheckReturnValue
public final class ImmutableUserDto extends UserDto {
...
...
private final String password;
...
...
/**
* #return The value of the {#code password} attribute
*/
#JsonProperty("password")
#JsonIgnore
#Override
public String getPassword() {
return password;
}
...
...
...
#Generated(from = "UserDto", generator = "Immutables")
#NotThreadSafe
public static final class Builder {
...
...
private String password;
#JsonProperty("password")
#JsonIgnore
public final Builder setPassword(String password) {
this.password = password;
return this;
}
}
}
Serialization will work as expected. The password attribute will be excluded from the JSON. But when I try to de-serialize, I get the following error:
java.lang.IllegalStateException: Cannot build UserDto, some of the required attributes are not set [password]
Which is obvious as Immutables added the #JsonIgnore to the setter as well.
The documentation isn't of much help. In their Things to be aware of section, it just mentions the following regarding #JsonIgnore
If using #JsonIgnore, you should explicitly make an attribute
non-mandatory. In Immutables, an attribute can be declared as
non-mandatory via #Nullable, Optional or #Value.Default which are all
different in their effect and we do not derive anything automatically.
Using #Nullable or Optional or #Value.Default is not of any use in case of fields like password.
I have gone through the issue list on their GitHub page and there is a similar issue but the user was asking for a slightly different use case and using #Nullable could solve the problem which doesn't work in my case.
I have also tried to use one of the answers here. Still resulted in the same error.
It looked like this is not supported by Immutables library. I have created a new issue myself. Once I get some feedback from users on SOF, I will probably create a sscce.
I had to use the suggestion given by #benarena in this comment. However I had to explicitly specify the value attribute of the property along with the Access attribute.
#JsonProperty(value = "password", access = JsonProperty.Access.WRITE_ONLY) solved the problem.
The Immutable class would look like:
#Value.Immutable
#Value.Style(defaults = #Value.Immutable(copy = false), init = "set*")
#JsonSersonIgnoreialize(as = ImmutableUserDto.class)
#JsonDeserialize(builder = ImmutableUserDto.Builder.class)
public abstract class UserDto {
....
#JsonProperty(value = "password", access = JsonProperty.Access.WRITE_ONLY)
public abstract String getPassword();
}
I have en entity class which has an natural ID field mapped as #Id and I don't have any surrogate ID(invented field only for table ID) field. And, in the Jackson marshalled JSON I see an extra id exposed.
So instead of:
{
"bin":"123456", ...
}
I see:
{
"id":"123456", "bin":"123456", ...
}
which I don't want because they are repeated information. How can I prevent this?
I haven't touched REST/MVC configuration adapter; they are for exposing the ID classes, but I don't want that.
Bean:
#Entity
#Data
#Table(name="bin_info")
public class BinInfo implements Serializable, Persistable<String> {
#Id
#NotBlank //this is for absent parameter. Not equal to Pattern regex check
#Pattern(regexp = "^\\d{6,8}$") //6-8 digits
#Column(name="bin")
#JsonProperty("bin")
private String bin;
...
I am with these dependencies:
dependencies {
compile('org.springframework.boot:spring-boot-starter-actuator')
compile('org.springframework.boot:spring-boot-starter-aop')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-data-rest')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-undertow')
runtime('com.h2database:h2')
runtime('org.postgresql:postgresql')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('io.cucumber:cucumber-java:3.0.2')
testCompile('io.cucumber:cucumber-junit:3.0.2')
testCompile('io.cucumber:cucumber-spring:3.0.2')
}
Spring Boot 2.0.3.
Try annotating with #NaturalId instead of #Id.
Thanks all, I think it has to do with some configuration of Spring or Jackson that will automatically expose the field mapped with #Id. I can just guess because no time for a confirmation.
And some colleague suggests me to define a DTO instead of putting the #Jsonxxx annotations in the class, saying the model represents data model and are related to the table, while DTO is related with view layer. So I did it and now all is fine.
Now the model is free of id field and #JsonProperty/#JsonIgnore:
#Entity
#Data
#Table(name="bin_info")
public class BinInfo implements Serializable, Persistable<String> {
#Id
#NaturalId
#NotBlank //this is for absent parameter. Not equal to Pattern regex check
#Pattern(regexp = "^\\d{6,8}$") //6-8 digits
#Column(name="bin")
//#JsonProperty("bin")
private String bin;
...
And the DTO is totally without #Id:
#Data
public class BinInfoDTO {
#JsonProperty("bin")
private String bin;
#JsonProperty("json_full")
private String json_full;
...
When I retrieve an entity, with a mapping method I set all values I need in a DTO to the DTO and return it to the endpoint. Then the JSON is normal and fine.
You have to implement the get and set from id field.
Try annotating that field with #JsonIgnore
I want to save user data to database. play version 2.5.3
I am getting this error:
JSR-303 validated property 'first_name' does not have a corresponding accessor for data binding - check your DataBinder's
configuration (bean property versus direct field access)]
My model class
#Entity
public class UserRegisterModel extends Model
{
#Id
#GeneratedValue
protected Long ID;
#Constraints.Required
protected String first_name;
protected String last_name;
protected String user_name;
#Constraints.Required
protected String password;
protected String password_confirmation;
#Constraints.Email
#Column(unique = true)
protected String email;
}
Controller class
public Result submitUserRegistrationForm()
{
play.data.Form<UserRegisterModel> form = play.data.Form.form(UserRegisterModel.class).bindFromRequest();
UserRegisterModel register = form.bindFromRequest().get();
}
Also I want to match password and conform password. I should do this in Model or controller.
Could you please provide me some sample code(Model,Controller) with form validation?
As discussed at the comments, you have to add play-enhancer as documented here:
https://www.playframework.com/documentation/2.5.x/PlayEnhancer#Setting-up
Also, the enhancer just works under the following conditions:
The enhancer looks for all fields on Java classes that:
are public
are non static
are non final
For each of those fields, it will generate a getter and a setter if they don’t already exist. If you wish to provide a custom getter or setter for a field, this can be done by just writing it, the Play enhancer will simply skip the generation of the getter or setter if it already exists.
So, you have two options here: keep the fields protected and write your own getters and setters or made the public and let the enhancer generate getters and setters required by other libraries (like form binding).
I have a field that has three annotations validation
#MyCustomValidation.List(
{
#MyCustomValidation(
fieldName = "hasAddress",
fieldValue = "false",
dependFieldName = "address")
})
public class Example {
#NotEmpty
#Length(min=3,max=100)
String address;
Boolean hasAddress;
I want to know know how to hibernate validator can ignore other validations when myCustomerValidation returns true in certain situations.
In this case, if and only if hasAddress field has true value should be validated the length of the address, otherwise it should not do any validation
I appreciate any help.