Merge pull request #13035 from thibaultfaure/article/BAEL-5940-object-validation-after-deserialization

BAEL-5940 code for the object validation after deserialization article
This commit is contained in:
davidmartinezbarua 2022-11-21 21:06:33 -03:00 committed by GitHub
commit 66f1799b72
23 changed files with 408 additions and 0 deletions

6
javax-validation-advanced/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.classpath
.project
.settings/
target/
bin/

View File

@ -0,0 +1,5 @@
## Java Bean Validation Examples
This module contains articles about Bean Validation.
### Relevant Articles:

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>javax-validation-advanced</artifactId>
<version>0.1-SNAPSHOT</version>
<name>javax-validation-advanced</name>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.databind.version}</version>
</dependency>
</dependencies>
<properties>
<spring.boot.version>2.7.5</spring.boot.version>
<jackson.databind.version>2.14.0</jackson.databind.version>
</properties>
</project>

View File

@ -0,0 +1,20 @@
package com.baeldung.javaxval.afterdeserialization;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
public class BeanDeserializerModifierWithValidation extends BeanDeserializerModifier {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
if (deserializer instanceof BeanDeserializer) {
return new BeanDeserializerWithValidation((BeanDeserializer) deserializer);
}
return deserializer;
}
}

View File

@ -0,0 +1,40 @@
package com.baeldung.javaxval.afterdeserialization;
import java.io.IOException;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBase;
public class BeanDeserializerWithValidation extends BeanDeserializer {
private static final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
private static final Validator validator = factory.getValidator();
protected BeanDeserializerWithValidation(BeanDeserializerBase src) {
super(src);
}
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
Object instance = super.deserialize(p, ctxt);
validate(instance);
return instance;
}
public <T> void validate(T t) {
Set<ConstraintViolation<T>> violations = validator.validate(t);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}

View File

@ -0,0 +1,14 @@
package com.baeldung.javaxval.afterdeserialization;
import javax.validation.constraints.Size;
public class Student {
@Size(min = 5, max = 10, message = "Student's name must be between 5 and 10 characters")
private String name;
public String getName() {
return name;
}
}

View File

@ -0,0 +1,24 @@
package com.baeldung.javaxval.afterdeserialization;
import java.io.IOException;
import java.io.InputStream;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class StudentDeserializerWithValidation {
public static Student readStudent(InputStream inputStream) throws IOException {
ObjectMapper mapper = getObjectMapperWithValidation();
return mapper.readValue(inputStream, Student.class);
}
private static ObjectMapper getObjectMapperWithValidation() {
SimpleModule validationModule = new SimpleModule();
validationModule.setDeserializerModifier(new BeanDeserializerModifierWithValidation());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(validationModule);
return mapper;
}
}

View File

@ -0,0 +1,51 @@
package com.baeldung.javaxval;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException;
import java.io.InputStream;
import javax.validation.ConstraintViolationException;
import org.junit.jupiter.api.Test;
import com.baeldung.javaxval.afterdeserialization.Student;
import com.baeldung.javaxval.afterdeserialization.StudentDeserializerWithValidation;
public class StudentDeserializerWithValidationUnitTest {
private final String EXPECTED_ERROR_MESSAGE = "name: Student's name must be between 5 and 10 characters";
private final String EXPECTED_STUDENT_NAME = "Daniel";
private final String NAME_TOO_LONG_STUDENT_FILE = "nameTooLongStudent.json";
private final String NAME_TOO_SHORT_STUDENT_FILE = "nameTooShortStudent.json";
private final String SUBDIRECTORY = "afterdeserialization/";
private final String VALID_STUDENT_FILE = "validStudent.json";
@Test
void givenValidStudent_WhenReadStudent_ThenReturnStudent() throws IOException {
InputStream inputStream = getInputStream(VALID_STUDENT_FILE);
Student result = StudentDeserializerWithValidation.readStudent(inputStream);
assertEquals(EXPECTED_STUDENT_NAME, result.getName());
}
@Test
void givenStudentWithTooShortName_WhenReadStudent_ThenThrows() {
InputStream inputStream = getInputStream(NAME_TOO_SHORT_STUDENT_FILE);
ConstraintViolationException constraintViolationException = assertThrows(ConstraintViolationException.class, () -> StudentDeserializerWithValidation.readStudent(inputStream));
assertEquals(EXPECTED_ERROR_MESSAGE, constraintViolationException.getMessage());
}
@Test
void givenStudentWithTooLongName_WhenReadStudent_ThenThrows() {
InputStream inputStream = getInputStream(NAME_TOO_LONG_STUDENT_FILE);
ConstraintViolationException constraintViolationException = assertThrows(ConstraintViolationException.class, () -> StudentDeserializerWithValidation.readStudent(inputStream));
assertEquals(EXPECTED_ERROR_MESSAGE, constraintViolationException.getMessage());
}
private InputStream getInputStream(String fileName) {
return getClass().getClassLoader()
.getResourceAsStream(SUBDIRECTORY + fileName);
}
}

View File

@ -0,0 +1,3 @@
{
"name": "Constantine"
}

View File

@ -0,0 +1,3 @@
{
"name": "Max"
}

View File

@ -0,0 +1,3 @@
{
"name": "Daniel"
}

6
javaxval2/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.classpath
.project
.settings/
target/
bin/

5
javaxval2/README.md Normal file
View File

@ -0,0 +1,5 @@
## Java Bean Validation Examples
This module contains articles about Bean Validation.
### Relevant Articles:

34
javaxval2/pom.xml Normal file
View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>javaxval2</artifactId>
<version>0.1-SNAPSHOT</version>
<name>javaxval2</name>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.databind.version}</version>
</dependency>
</dependencies>
<properties>
<spring.boot.version>2.7.5</spring.boot.version>
<jackson.databind.version>2.14.0</jackson.databind.version>
</properties>
</project>

View File

@ -0,0 +1,20 @@
package com.baeldung.javaxval.afterdeserialization;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
public class BeanDeserializerModifierWithValidation extends BeanDeserializerModifier {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
if (deserializer instanceof BeanDeserializer) {
return new BeanDeserializerWithValidation((BeanDeserializer) deserializer);
}
return deserializer;
}
}

View File

@ -0,0 +1,40 @@
package com.baeldung.javaxval.afterdeserialization;
import java.io.IOException;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBase;
public class BeanDeserializerWithValidation extends BeanDeserializer {
private static final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
private static final Validator validator = factory.getValidator();
protected BeanDeserializerWithValidation(BeanDeserializerBase src) {
super(src);
}
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
Object instance = super.deserialize(p, ctxt);
validate(instance);
return instance;
}
public <T> void validate(T t) {
Set<ConstraintViolation<T>> violations = validator.validate(t);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}

View File

@ -0,0 +1,14 @@
package com.baeldung.javaxval.afterdeserialization;
import javax.validation.constraints.Size;
public class Student {
@Size(min = 5, max = 10, message = "Student's name must be between 5 and 10 characters")
private String name;
public String getName() {
return name;
}
}

View File

@ -0,0 +1,24 @@
package com.baeldung.javaxval.afterdeserialization;
import java.io.IOException;
import java.io.InputStream;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class StudentDeserializerWithValidation {
public static Student readStudent(InputStream inputStream) throws IOException {
ObjectMapper mapper = getObjectMapperWithValidation();
return mapper.readValue(inputStream, Student.class);
}
private static ObjectMapper getObjectMapperWithValidation() {
SimpleModule validationModule = new SimpleModule();
validationModule.setDeserializerModifier(new BeanDeserializerModifierWithValidation());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(validationModule);
return mapper;
}
}

View File

@ -0,0 +1,51 @@
package com.baeldung.javaxval;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException;
import java.io.InputStream;
import javax.validation.ConstraintViolationException;
import org.junit.jupiter.api.Test;
import com.baeldung.javaxval.afterdeserialization.Student;
import com.baeldung.javaxval.afterdeserialization.StudentDeserializerWithValidation;
public class StudentDeserializerWithValidationUnitTest {
private final String EXPECTED_ERROR_MESSAGE = "name: Student's name must be between 5 and 10 characters";
private final String EXPECTED_STUDENT_NAME = "Daniel";
private final String NAME_TOO_LONG_STUDENT_FILE = "nameTooLongStudent.json";
private final String NAME_TOO_SHORT_STUDENT_FILE = "nameTooShortStudent.json";
private final String SUBDIRECTORY = "afterdeserialization/";
private final String VALID_STUDENT_FILE = "validStudent.json";
@Test
void givenValidStudent_WhenReadStudent_ThenReturnStudent() throws IOException {
InputStream inputStream = getInputStream(VALID_STUDENT_FILE);
Student result = StudentDeserializerWithValidation.readStudent(inputStream);
assertEquals(EXPECTED_STUDENT_NAME, result.getName());
}
@Test
void givenStudentWithTooShortName_WhenReadStudent_ThenThrows() {
InputStream inputStream = getInputStream(NAME_TOO_SHORT_STUDENT_FILE);
ConstraintViolationException constraintViolationException = assertThrows(ConstraintViolationException.class, () -> StudentDeserializerWithValidation.readStudent(inputStream));
assertEquals(EXPECTED_ERROR_MESSAGE, constraintViolationException.getMessage());
}
@Test
void givenStudentWithTooLongName_WhenReadStudent_ThenThrows() {
InputStream inputStream = getInputStream(NAME_TOO_LONG_STUDENT_FILE);
ConstraintViolationException constraintViolationException = assertThrows(ConstraintViolationException.class, () -> StudentDeserializerWithValidation.readStudent(inputStream));
assertEquals(EXPECTED_ERROR_MESSAGE, constraintViolationException.getMessage());
}
private InputStream getInputStream(String fileName) {
return getClass().getClassLoader()
.getResourceAsStream(SUBDIRECTORY + fileName);
}
}

View File

@ -0,0 +1,3 @@
{
"name": "Constantine"
}

View File

@ -0,0 +1,3 @@
{
"name": "Max"
}

View File

@ -0,0 +1,3 @@
{
"name": "Daniel"
}

View File

@ -415,6 +415,7 @@
<module>javax-sound</module>
<module>javaxval</module>
<module>javaxval-2</module>
<module>javax-validation-advanced</module>
<module>jaxb</module>
<module>jersey</module>
<module>jgit</module>
@ -820,6 +821,7 @@
<module>javax-sound</module>
<module>javaxval</module>
<module>javaxval-2</module>
<module>javax-validation-advanced</module>
<module>jaxb</module>
<module>jersey</module>
<module>jgit</module>