Is it possible to use #Valid (javax.validation.Valid) in below scenario?
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.validation.Valid;
import com.incident.tool.model.IncidentModel;
#Service
public class JsonStringToObjectConverter {
public IncidentModel convertToObject(String json) throws JsonMappingException, JsonProcessingException {
#Valid
IncidentModel incidentModel = new ObjectMapper().readValue(json, IncidentModel.class);
return incidentModel ;
}
}
Here JsonStringToObjectConvertor is taking in JSON in form of String and mapping it to IncidentModel class. I have defined few validations in IncidentModel in below manner and I want to validate the fields mapped by ObjectMapper in IncidentModel before proceeding further:
#Component
#Getter
#Setter
#ToString
#NoArgsConstructor
#AllArgsConstructor
public class IncidentModel extends IncidentInfo {
#NotEmpty
private String empId;
#NotEmpty
#Size(min = 2, max = 30)
private String empName;
#NotEmpty
private String title;
private String description;
private String assignedTo;
private String severity;
private String incidentNumber;
private String dateCreated;
private String dateClosed;
private String closingNotes;
}
It does not seem to work in the above format, is there any alternative to use the #Valid in the convertToObject method?
Thanks for your help.
You can do something as follows:
#Service
public class JsonStringToObjectConverter {
public IncidentModel convertToObject(String json) throws JsonMappingException, JsonProcessingException {
IncidentModel incidentModel = new ObjectMapper().readValue(json, IncidentModel.class);
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<IncidentModel>> errors = validator.validate(incidentModel);
return incidentModel;
}
}
You could then optimize this and make ValidatorFactory factory and Validator validator instance variables of JsonStringToObjectConverter so that you don't recreate them every time you call convertToObject method.
Related
I'm trying to implement the server side validation using spring. but its not validating. Here is my code sample.
#RestController
#RequestMapping("/api/v1/note")
public class NoteController {
#Autowired
private final NoteService noteService;
#PostMapping
public ResponseEntity<String> create(#Valid #RequestBody final NoteDto noteDto){
noteService.create(noteDto);
return new ResponseEntity<>("sucess", HttpStatus.CREATED);
}
}
POJO..
#Data
#JsonInclude(value = Include.NON_NULL)
public class NoteDto {
#NotEmpty(message = "Building No can't be empty!")
private String buildingNo;
private String buildingName;
#NotEmpty(message = "Street can't be empty!")
}
What am missing here
#Valid annotation that triggers validations on the NoteDto (in this case #NotNull and #Future). These annotations could come from different JSR-303 providers (e.g, Hibernate, Spring..etc).
Example
static class NoteDto {
#NotNull #Future
private Date date;
}
And Remove final.
I am consuming a rest API in Spring Boot but I am getting this error -
java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy
On rigorous debugging, I found it was because my domain class does not have a key that I am getting in API response "developer.email".
I have #JsonIgnoreProperties(ignoreUnknown = true) annotation in the domain class.
Java does not let you create a variable name with "developer.email"
How do I make it work? Is there a workaround in spring framework for such cases?
Is there a way to manually configure this entity instead of spring autoconfigure.
This is the JSON that I am trying to parse
{"developer.email" : "dev.accounts+developerapps#domain.com"}
This is my current domain class, I am using lombok here.
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
#JsonIgnoreProperties(ignoreUnknown = true)
#Data
public class OauthTokenResponse {
private String refresh_token_expires_in;
private String refresh_token_status;
private String api_product_list;
private String app_enduser;
#JsonIgnore
private List<String> api_product_list_json;
private String organization_name;
#JsonIgnore
#JsonProperty("developer.email")
private String developerEmail;
private String token_type;
private String issued_at;
private String client_id;
private String access_token;
private String refresh_token;
private String application_name;
private String scope;
private String refresh_token_issued_at;
private String expires_in;
private String refresh_count;
private String status;
}
Did you try annotating the field with '#JsonProperty' ?
#JsonProperty("developer.email")
String developerEmail
Small test to prove my point:
public class Evil {
#JsonProperty("evil.property")
public int evil;
}
#RunWith(MockitoJUnitRunner.class)
public class MapperTest {
#Test
public void mapperTest() throws JsonParseException, JsonMappingException, IOException {
final ObjectMapper mapper = new ObjectMapper();
final String test = "{ \"evil.property\" : 2 }";
final Evil evil = mapper.readValue(test, Evil.class);
assertThat(evil.evil, is(2));
}
}
Is there a way to ignore this key?
You can use #JsonIgnoreProperties(value = "developer.email") annotation to ignore this property.
I use Jackson for serialization/deserialization with my Spring Boot project.
I have a DTO object with the following structure,
public class TestDTO implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private UUID certificateId;
#NotNull
private Long orgId;
#NotNull
private CertificateType certificateType;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
#Valid
#NotNull
private PublicCertificateDTO publicCertificate;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
#Valid
private PrivateCertificateDTO privateCertificate;
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private ZonedDateTime expiryDate;
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private ZonedDateTime createdDate;
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private ZonedDateTime updatedDate;
}
Serialization of this object in my unit tests with the following method,
public static byte[] convertObjectToJsonBytes(TestDTO object)
throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
JavaTimeModule module = new JavaTimeModule();
mapper.registerModule(module);
return mapper.writeValueAsBytes(object);
}
causes fields with WRITE_ONLY access to get ignored (for obvious reasons). So in the serialized object I see null values for publicCertificate and privateCertificate.
I did try setting mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
Is there any other way to ignore these properties for Unit Tests ?
While the solution specified works, it is an overkill for the requirement. You don't need custom serializers if all you want is to override annotations. Jackson has a mixin feature for such trivial requirements
Consider the following simplified POJO:
public class TestDTO
{
public String regularAccessProperty;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
public String writeAccessProperty;
}
If you want to override the #JsonProperty annotation, you create another POJO that has a variable with the exact same name (or same getter/setter names):
// mixin class that overrides json access annotation
public class UnitTestDTO
{
#JsonProperty(access = JsonProperty.Access.READ_WRITE)
public String writeAccessProperty;
}
You associate the original POJO and the mixin via a Simplemodule:
simpleModule.setMixInAnnotation(TestDTO.class, UnitTestDTO.class);
Is there any other way to ignore these properties for Unit Tests ?
Solution: In your convertObjectToJsonBytes method, you can use:
mapper.disable(MapperFeature.USE_ANNOTATIONS);
Reference: MapperFeature.USE_ANNOTATIONS
/**
* Feature that determines whether annotation introspection
* is used for configuration; if enabled, configured
* {#link AnnotationIntrospector} will be used: if disabled,
* no annotations are considered.
*<p>
* Feature is enabled by default.
*/
USE_ANNOTATIONS(true),
Note: This will disable all annotations for given ObjectMapper.
Another solution is to override the annotation inspector with a simple custom class. That would be the minimal example:
ObjectMapper mapper = new ObjectMapper().setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
#Override
public JsonProperty.Access findPropertyAccess(Annotated m) {
return null;
}
});
Other solution for Spring Boot #Autowired object mappers:
Use a dedicated class so it's reusable and more readable:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
public class IgnoreReadOnlyFieldsAnnotationInspector extends JacksonAnnotationIntrospector {
#Override
public JsonProperty.Access findPropertyAccess(Annotated m) {
return null;
}
}
Within the test use #BeforeEach (or her older friends)
public class AmazingTest {
#Autowired
ObjectMapper mapper;
#BeforeEach
void beforeAll(){
// need to copy because the autowired mapper in test and the object mapper in code under test are the same instance
mapper = objectMapper.copy();
mapper.setAnnotationIntrospector(new IgnoreReadOnlyFieldsAnnotationInspector());
}
}
This was solved by adding a custom serializer for the JUnit tests.
So for TestDTO I added the serializer as below.
private class TestJsonSerializer extends StdSerializer<TestDTO> {
public TestJsonSerializer() {
this(null);
}
public TestJsonSerializer(Class<TestDTO> t) {
super(t);
}
#Override
public void serialize(TestDTO value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeNumberField("orgId", value.getOrgId());
gen.writeStringField("certificateType", value.getCertificateType().getType());
if (value.getPublicCertificate() != null) {
gen.writeObjectField("publicCertificate", value.getPublicCertificate());
}
if (value.getPrivateCertificate() != null) {
gen.writeObjectField("privateCertificate", value.getPrivateCertificate());
}
gen.writeObjectField("expiryDate", value.getExpiryDate());
gen.writeObjectField("createdDate", value.getCreatedDate());
gen.writeObjectField("updatedDate", value.getUpdatedDate());
gen.writeEndObject();
}
}
I then added,
ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(TestDTO.class, new TestJsonSerializer());
mapper.registerModule(simpleModule);
Similarly added and registered custom serializers for nested objects, publicCertificate and privateCertificate.
Here a simple example
#ToString
#Getter
#Setter
public class Account implements Cloneable {
#JsonProperty(access = Access.WRITE_ONLY)
private Integer accountId;
private String accountType;
private Long balance;
public AccountTest clone() {
AccountTest test = new AccountTest();
test.setAccountId(this.accountId);
test.setAccountType(this.accountType);
test.setBalance(this.balance);
return test;
}
}
#ToString
#Getter
#Setter
public class AccountTest {
private Integer accountId;
private String accountType;
private Long balance;
}
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
try {
Account account = new Account();
account.setAccountId(1999900);
account.setAccountType("Saving");
account.setBalance(2433l);
AccountTest accountTest = account.clone();
System.out.println(account);
byte[] accountBytes = mapper.writeValueAsBytes(account);
System.out.println(new String(accountBytes));
byte[] accountTestBytes = mapper.writeValueAsBytes(accountTest);
System.out.println(new String(accountTestBytes));
} catch (IOException e) { }
}
}
I would like to define my custom serialization strategy (which fields to include), while using Jackson. I know, that I can do it with views/filters, but it introduces very bad thing - using string-representation of field names, which automatically enables problems with auto-refactoring.
How do I force Jackson into serializing only annotated properties and nothing more?
If you disable all auto-detection it should only serialize the properties that you have annotated--whether it be the properties themselves or the getters. Here's a simple example:
private ObjectMapper om;
#Before
public void setUp() throws Exception {
om = new ObjectMapper();
// disable auto detection
om.disable(MapperFeature.AUTO_DETECT_CREATORS,
MapperFeature.AUTO_DETECT_FIELDS,
MapperFeature.AUTO_DETECT_GETTERS,
MapperFeature.AUTO_DETECT_IS_GETTERS);
// if you want to prevent an exception when classes have no annotated properties
om.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
}
#Test
public void test() throws Exception {
BlahClass blahClass = new BlahClass(5, "email", true);
String s = om.writeValueAsString(blahClass);
System.out.println(s);
}
public static class BlahClass {
#JsonProperty("id")
public Integer id;
#JsonProperty("email")
public String email;
public boolean isThing;
public BlahClass(Integer id, String email, boolean thing) {
this.id = id;
this.email = email;
isThing = thing;
}
}
In case you want to do this without configuring the mapper just for a specific type:
#JsonAutoDetect(
fieldVisibility = Visibility.NONE,
setterVisibility = Visibility.NONE,
getterVisibility = Visibility.NONE,
isGetterVisibility = Visibility.NONE,
creatorVisibility = Visibility.NONE
)
public class BlahClass {
#JsonProperty("id")
private Integer id;
#JsonProperty("email")
private String email;
}
I have Pojo object, with getAsJson function to return Json string for this object.
I use JsonProperty to define json properties in this object.
Use writeValueAsString of ObjectMapper to write json string for this object.
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
#JsonIgnoreProperties(ignoreUnknown=true)
public class LogLikeArticleDetail extends BaseObject {
private static final long serialVersionUID = -2018373118257019033L;
#JsonProperty("LikeArticleGUId")
private String likeArticleGUId;
#JsonProperty("UserId")
private String userID;
#JsonProperty("UserName")
private String userName;
#JsonProperty("IP")
private String ip;
#JsonProperty("OS")
private String os;
#JsonProperty("UserAgent")
private String userAgent;
#JsonProperty("WebsiteCode")
private String websiteCode;
#JsonProperty("ArticleId")
private String articleID;
#JsonProperty("ATitle")
private String aTitle;
#JsonProperty("CateAlias")
private String cateAlias;
#JsonProperty("LikeStatus")
private String likeStatus;
#JsonProperty("TimeStamp")
private Date timeStamp;
//get, set....
//....
#JsonIgnore
public String getAsJSON() throws JsonGenerationException, JsonMappingException, IOException{
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(this) ;
}
}
Now, i get result
public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
Calendar calendar = Calendar.getInstance();
LogLikeArticleDetail logLikeArticle = new LogLikeArticleDetail("1","2","3","4","5","6","7","8","what thing \"nothing\" show","10","11",calendar.getTime());
System.out.println(logLikeArticle.getAsJSON());
}
But the result's duplicated properties:
{"LikeArticleGUId":"1","UserId":"2","UserName":"3","IP":"4","OS":"5","UserAgent":"6","WebsiteCode":"7","ArticleId":"8","ATitle":"what thing \"nothing\" show","CateAlias":"10","LikeStatus":"11","TimeStamp":1352256727062,"_likeArticleGUId":"1","websiteCode":"7","likeStatus":"11","userID":"2","userName":"3","ip":"4","os":"5","userAgent":"6","articleID":"8","aTitle":"what thing \"nothing\" show","cateAlias":"10","timeStamp":1352256727062}
Show me what's occur in this problem ?
So i do follow:
how to specify jackson to only use fields - preferably globally
I add
#JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
before
public class LogLikeArticleDetail extends BaseObject
and the result that i want.
So can another solve that in getAsJson() function like:
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibilityChecker(mapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
return mapper.writeValueAsString(this) ;
Thanks for #Sean Carpenter 's question and #kmb385 answer in link above.
You can also do this per POJO using annotations. Add this string to the top of your class you'd like no auto detection on:
#JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY,
getterVisibility=JsonAutoDetect.Visibility.NONE,
setterVisibility=JsonAutoDetect.Visibility.NONE,
creatorVisibility=JsonAutoDetect.Visibility.NONE)
For example:
#JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY, getterVisibility=JsonAutoDetect.Visibility.NONE,
setterVisibility=JsonAutoDetect.Visibility.NONE, creatorVisibility=JsonAutoDetect.Visibility.NONE)
class Play {
#JsonProperty("Name")
private String name;
#JsonProperty("NickName")
private String nickName;
public Play(){
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
}
This will return the properties I've defined and not auto-detect the field names and add them to my returned JSON result.
We can also use the #JsonProperty("Name") annotation directly on the getters to avoid duplication.
It is actually not an issue. So, over here what happened was Jackson library was unable to match those fields automatically (there is no assumption of case unification), so you end up with twice the properties you expect.
The simple fix for this issue is to just add annotations to either getters/setters (either is fine.)
#JsonProperty("UserName")
public String getUserName() {
return this.userName;
}
This issue was also raised in Jackson Github repo. You can find the answer in the following link.
https://github.com/FasterXML/jackson-databind/issues/1609