I am trying to validate a json when deserialize it using constraints annotations but it doesn't seem to work. for example
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
public class SimpleFooRequest {
#NotNull
#JsonProperty(value = "count")
#Min(value = 100,message = "must be min 10")
private Integer count;
#NotNull
private String name;
public String getName() {return name; }
public void setName(String name) { this.name = name; }
public Integer getCount() {return count;}
public void setCount(Integer count) {this.count = count;}
}
when trying to unmarshal the json
#Test
public void testFooRequest() throws Exception
{
String fooJson = {"count":-10}
ObjectMapper mapper = new ObjectMapper()
SimpleFooRequest request = mapper.readValue(fooJson,SimpleFooRequest.class);
assert request.getCount().equals(-10);//expected fail but it pass !!!
}
it seems to pass although I expected that the assertion will fail due to the count min value limitation . I also tried to put the annotations on the setters but got same results
I have <mvc:annotation-driven /> and hibernate-validator-4.3.1.Final.jar in my dependencies
I know that I can create custom validator but I thought that for simple validations I can use the annotations .
what do I need to do to make it work ?
Annotations are just meta-data. They're not programs that can perform any validation. The validator is the program that will make use of the meta-data. In a container, the container will manage the validator. In basic unit tests or standalones, you will need to manage the validator yourself.
That being said, even a validator cannot stop the object from being created/populated (as you seem to be asserting in your test). It just validates that the value is valid. If not, the validator throws an exception and/or lists some constraint violations. Either the container, or you should handle what to do with the exceptions/violations.
And AFAIK, there's no ObjectMapper configuration for automatic JSR-303 support while deserializing
A better unit test, might look something like
/* ----- Using the Hibernate Validator implementation ----- */
#Test
public void testInvalidCount() throws Exception {
final String json = "{\"count\":-10, \"name\":\"Stack\"}";
ObjectMapper mapper = new ObjectMapper();
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
SimpleFooRequest request = mapper.readValue(
json, SimpleFooRequest.class);
Set<ConstraintViolation<SimpleFooRequest>> violations
= validator.validate(request);
Assert.assertEquals(1, violations.size());
Assert.assertEquals("must be min 10",
violations.iterator().next().getMessage());
}
See Getting started with Hibernate Validator for some more examples
How about validating at the setter method?
Jackson calls your setters if exists...
Related
Suppose I have a class, what is the order of validation in a SpringBoot class object. After an object gets created then the fields are populated or does the validation happens before the objects are populated, at the time of setting of the field values this validation happens. Or after the object is created then by a get call we validate the object field values.
package com.bablo.google.request;
import java.io.Serializable;
import java.util.Set;
import javax.validation.constraints.NotNull;
public class SomeRequest implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#NotNull
private Long userId;
private String resNote; //this is not annotated with #NotNull
#NotNull
private String revTag;
public Long getUserId() {
return userId;
}
public void setUserId(final Long userId) {
this.userId = userId;
}
public String getResNote() {
return responseNote;
}
public void setResNote(final String resNote) {
this.resNote = resNote.trim(); //Call to trim() method of the String object.
}
public String getRevTag() {
return revTag;
}
public void setRevTag(final String revTag) {
this.revTag = revTag.trim(); //Call to the trim() method of the String object.
}
}
What is the way that validation will happen in a class? What is the mechanism of validating the fields, does the #NotNull validation or for that matter any validation depends on the getter methods to do the validation?
Do they first call the setter methods to do the validation?
Splitting your questions and adding answers.
What is the order of validation in a SpringBoot class object?
Validation happens as part of data binding process. Every request parameter/path variable will be validated as per the marked annotation and only when the validation passes, the value will be assigned to the class object.
What is the way that validation will happen in a class?
Validation process differs for each binding mechanism. If the method parameter is ModelAttribute/request parameter/path variable/Map etc. Spring uses different argument resolvers for each method parameter. If #Valid is added, then it enables validation during argument resolution process (Look out for RequestMappingHandlerAdapter where the whole magic is wired).
Does the #NotNull validation or for that matter any validation depends on the getter methods to do the validation? Do they first call the setter methods to do the validation?
Spring uses reflection to construct/validate the method argument class. Data binding and validation happens even without getters/setters.
You can validate manually by calling
#Autowired
private javax.validation.Validator validator;
...
validator.validate(new SomeRequest()); // you can also pass method's argument
or you can use auto validation
Here is an example https://www.baeldung.com/spring-boot-bean-validation of using #Valid + #ExceptionHandler
Here is an example https://spring.io/guides/gs/validating-form-input/ of using #Valid + BindingResult
I am using springBoot 2 and I am trying to validate the objects in a List via:
#RequestMapping(value = "/bets",
produces = {"application/json"},
consumes = {"application/json"},
method = RequestMethod.POST
)
void postBets(#RequestBody List<#Valid Bet> bets);
and Bet class has #NotNull annotations on certain attributes.
import javax.validation.constraints.NotNull;
public class Bet extends BetMessage {
#NotNull
private String categoryName;
#NotNull
private String marketName = null;
#NotNull
private OffsetDateTime startTime = null;
#NotNull
private String betName = null;
I have also added the spring-boot-starter-validation artifact to my build file but still no validation is happening.
As a workaround I have implemented the popular answer in the question below (ValidList class) and validation is working as expected; however I think that I am missing something obvious and the solution is now part of the validation library.
Validation of a list of objects in Spring
You may want to write a wrapper which contains your list of Bet because then your wrapper will conform to JavaBean specs and the validations can be applied.
Below Answer might help in this case.
#Valid on list of beans in REST service
I am working on a project that I need to put some limitations/constrains on the fields of the models(e.g. "String name" field should not exceed 10 characters) . I can only find Java Bean Validation API for this job. However as I see, it is used with Hibernate and Spring Framework.
Unfortunately, an ORM like Hibernate was not used in the project. We are using DAO pattern and JDBI for database operations.
Is there any alternative annotations on Java which helps to put constrains on the fields like Bean Validation does( and hopefully works with magic like Lombok does)? I need basically Size, Min/Max and NonNull annotations.
Basically something like this:
class User {
#Size(max = 10)
String name;
}
karelss already answered, you can also use javax.validation.constraints package here maven link. Here is possible implementation and test code (not perfect one).
User.java
import javax.validation.constraints.Size;
class User {
#Size(max = 10)
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
UserTest.java
import static org.junit.Assert.assertEquals;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.junit.Test;
public class UserTest {
#Test
public void test() {
User user = new User();
// set name over 10
user.setName("12345678912");
// validate the input
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<User>> violations = validator.validate(user);
for (ConstraintViolation v : violations) {
String key = "";
if (v.getPropertyPath() != null) {
key = v.getPropertyPath().toString();
assertEquals("name", key);
assertEquals("size must be between 0 and 10", v.getMessage());
}
}
assertEquals(1, violations.size());
}
}
Java Bean Validation API is the right tool for this job, but as you say is an api, if you are using an application server, you will have different implementations and you can use whatever you want, it's not linked to hibernate or spring, what you see are different providers of the api implementatión. This api works with objects, you can annotate any object with it.
If you don't want to include dependencies you can implement this validations in a compatible way using your own annotations like here
Java 7 Bean validation API
I am using Jackson 2.8.5 with ParameterNamesModule for Java 8 (https://github.com/FasterXML/jackson-modules-java8).
My problem is very specific for one use case, when I want to de-serialize a class with a single constructor using a single argument. Here is a test to reproduces the behavior:
public class JacksonTest {
#Test
public void TestReadValue() throws IOException {
ObjectMapper objectMapper = new ObjectMapper()
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
.setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.PUBLIC_ONLY)
.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES));
ImmutableIdentity identity = objectMapper.readValue("{\"id\":\"ABCDEF\"}", ImmutableIdentity.class);
assertEquals("ABCDEF", identity.id);
}
private static final class ImmutableIdentity {
private final String id;
public ImmutableIdentity(final String id) {
Objects.requireNonNull(id, "The id must not be null.");
this.id = id;
}
}
}
The test fails with the reason:
com.fasterxml.jackson.databind.JsonMappingException: Can not construct
instance of JacksonTest$ImmutableIdentity, problem: The id must not be
null. at [Source: {"id":"ABCDEF"}; line: 1, column: 15]
The funny thing is that if I add another argument to the constructor, the test passes OK.
public class JacksonTest {
#Test
public void TestReadValue() throws IOException {
ObjectMapper objectMapper = new ObjectMapper()
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
.setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.PUBLIC_ONLY)
.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES));
ImmutableIdentity identity = objectMapper.readValue("{\"id\":\"ABCDEF\"}", ImmutableIdentity.class);
assertEquals("ABCDEF", identity.id);
}
private static final class ImmutableIdentity {
private final String id;
public ImmutableIdentity(final String id, **final String unused**) {
Objects.requireNonNull(id, "The id must not be null.");
this.id = id;
}
}
}
I really don't like the idea to use a useless argument in the constructor here to make it less ambiguous, because it has no value in my business objects, especially they are for instance ProjectId, or some abstract Id that defines my entities and I need to construct them manually as well. So I would like to find a configuration of Jackson to support this but I could not.
I also crossposted here for the maintainers: https://github.com/FasterXML/jackson-modules-java8/issues/8
Are you by any chance compiling JacksonTest with -parameters option? If so, this is expected behavior.
Single argument constructors were historically used as delegating creators.
I've had discussions about this matter with #staxman even when we created the module. The issue popped up a number of times by various users, see this issue for details.
Looking to the future, this will hopefully be changed in 3.0, see this topic for details.
Update: regarding 3.0 change, see this issue. If you want this behavior to change please add +1 or comment. Right now it isn't clear if either approach is better as there are users that need the old behavior (see the issue for more details).
Suppose you have this entity:
class Foo{
String propA;
String propB;
}
and you want to serialize for one API like :
{propA: "ola",
propB: "Holla"}
and for another API like :
{fooPropA: "ola",
fooPropB: "Holla"}
How can this be achieved using jackson and using the same entity. Creating 2 different entities is not an option :)
There are several ways in which you can achieve this. You can enable a custom serializer (already covered by #se_vedem), register an annotation introspector which changes the property names for the corresponding class and so on.
However, if you are willing to only add a string prefix to all the property names, then the Jackson property name strategy is probably the best fit. The naming strategy class has the access to the serialized object type information, so you can make a decision whether to change the property name or not.
Here is an example using a custom annotation that defines the prefix:
public class JacksonNameStrategy {
#Retention(RetentionPolicy.RUNTIME)
public static #interface PropertyPrefix {
String value();
}
#PropertyPrefix("foo_")
public static class Foo {
public String propA;
public String propB;
public Foo(String propA, String propB) {
this.propA = propA;
this.propB = propB;
}
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new MyPropertyNamingStrategyBase());
System.out.println(mapper.writeValueAsString(new Foo("old", "Holla")));
}
private static class MyPropertyNamingStrategyBase extends PropertyNamingStrategy {
#Override
public String nameForField(MapperConfig<?> config,
AnnotatedField field,
String defaultName) {
PropertyPrefix ann = field.getDeclaringClass().getAnnotation(PropertyPrefix.class);
if (ann != null) {
return ann.value() + defaultName;
}
return super.nameForField(config, field, defaultName);
}
}
}
Output:
{"foo_propA":"old","foo_propB":"Holla"}
In your API method you choose between two ObjectMapper instances one with the default naming naming strategy and one with the custom one.
You can achieve this by using modules feature from Jackson.
Basically, each API would have it's own ObjectMapper and they will be configured with different modules. This way you can create 2 serializers for the same class and register them on the appropriate module. More read can be found here http://wiki.fasterxml.com/JacksonFeatureModules
However, be aware that serializers are loaded in a particular order. First it tries to get the annotated ones, if none is found it will try to get those registered from modules. So, for example if you have your class annotated with serializer, then that serializer(FooSerializer) would be chosen instead of the one configured in module(MySecondFooSerializer).
#JsonSerialize(using = FooSerializer.class)
class Foo{
String propA;
String propB;
}
module.addSerializer(Foo.class, new MySecondFooSerializer());