Java Jsonb #JsonbNillable not functioning as expected - java

I am using Jsonb annotations to consume and produce JSON in a RESTful service. While producing the JSON from a class and using #JsonbNillable it doesn't appear to return null value for a field. I know the below snippets are not the full code but for demonstration purposes only as there are other fields filled so there is JSON to return. I am wondering if I am using the annotations in an incorrect manner.
Expected JSON
{
"my_id" : null
}
Attempt 1:
The following will return the field if not null, but when null it will not display
public class MyClass {
#JsonbNillable
#JsonbProperty(value = "my_id")
public String myId;
}
Attempt 2:
The following will return the field if not null or null, however, the field name is just myId and not the expected "my_id".
public class MyClass {
#JsonbNillable
public String myId;
}
Attempt 3:
The following will return the field if not null or null and have the field in the proper syntax. The expected outcome here is exactly what I am looking for. My concern here is that the nillable inside the #JsonbProperty is marked as deprecated in favor of #JsonbNillable.
public class MyClass {
#JsonbProperty(value = "my_id", nillable = true)
public String myId;
}
It is like I cannot use #JsonbNillable and #JsonbProeprty together. Is this by design? Is there something else that should be done differently?
Thank you in advance for any advise.

Related

Spring Boot - how to validate fields that depend on each other?

Is there some way in Spring Boot that I can perform validation on properties that depend on each other's values, and have the error message be associated with the property?
I want to return the errors to the user in a nice JSON structure:
{
"errors": {
"name": "is required if flag is true"
}
}
Example:
#Entity
public class MyEntity {
private boolean nameRequiredFlag;
// Required if "nameRequiredFlag" is set to true:
private String name;
}
One solution that doesn't solve my problem of associating the error message with the name property is to create a validator annotation for the entity:
#ValidEntity
public class MyEntity {
private boolean nameRequiredFlag;
// Required if "nameRequiredFlag" is set to true:
private String name;
}
#Constraint( validatedBy = { MyEntityValidator.class } )
#Documented
#Target( { ElementType.TYPE } )
#Retention( RetentionPolicy.RUNTIME )
public #interface ValidEntity{
Class<?>[] groups () default {};
String message () default "name is required if 'nameRequiredFlag' is true";
Class<? extends Payload>[] payload () default {};
}
public class MyEntityValidator implements Validator<ValidEntity, MyEntity> {
#Override
public boolean isValid ( MyEntity entity, ConstraintValidatorContext context ) {
if ( !entity.nameRequiredFlag ) return true;
return !StringUtils.isBlank( entity.getName() );
}
}
This is laughably cumbersome and doesn't solve my problem. Isn't there any way I can do this with the framework validation?
Edit: This is for a JSON API, and the consumer really needs to be able to associate the error message to a best guess at which field has an issue. It is not helpful to send the consumer an error message for the whole object, or a computed property.
Solution given by #EvicKhaosKat is one way of doing it. However, when there are too many fields dependent on each other in a complicated way, your class becomes full of annotations and I personally struggle a lot relating them.
A simpler approach is to create a method(s) in your pojo which does the cross field validations and returns a boolean. On the top of this method annotate it with #AssertTrue(message = "your message"). It will solve your problem in a cleaner fashion.
public class SampleClass {
private String duration;
private String week;
private String month;
#AssertTrue(message = "Duration and time attributes are not properly populated")
public boolean isDurationCorrect() {
if (this.duration.equalsIgnoreCase("month")) {
if (Arrays.asList("jan", "feb", "mar").contains(month))
return true;
}
if (this.duration.equalsIgnoreCase("week")) {
if (Arrays.asList("1-7", "8-15", "16-24", "25-31").contains(week))
return true;
}
return false;
}
}
Note: I have not tested this code but have used this approach in multiple places and it works.
Possible reason is that name validation operates on not-yet-fully constructed object, so nameRequiredFlag is not filled yet.
As an option there is a #GroupSequence annotation, which allows to group and perform validations in an order you specify.
For example it is possible to add to MyEntity annotations:
#ValidEntity(groups = DependentValidations.class)
#GroupSequence({MyEntity.class, DependentValidations.class})
So all the other validation annotations on MyEntity class gonna be performed first, and after that DependentValidations group, which consists of ValidEntity.
Thus ValidEntity will be called on fully created object, and the last in order.
(DependentValidations.class - just an empty interface created somewhere nearby, like any other marker interface)
https://www.baeldung.com/javax-validation-groups will possibly describe that in much more details.
p.s. answer provided by #Innovationchef will possibly suit the case more :)

Handle Json non existent keys spring boot

I am creating a requestModel and let say a person doesn't send me some keys.
If that key is not present I want to put null if i get the value of the key.
I don't want to investigate if a key is present or not .
public class CustomerModel {
private Optional<String> s3Bucket;
private Optional<String> docType;
public String getS3Bucket() {
if(s3Bucket.isPresent()) {
return s3Bucket.get();
} else {
return null;
}
}
public void setS3Bucket(Optional<String> s3Bucket) {
this.s3Bucket = s3Bucket;
}
public Optional<String> getDocType() {
return docType;
}
public void setDocType(Optional<String> docType) {
this.docType = docType;
}
}
Do we have any library or something where.
1. If i get the key and it is not present in the coming request json, i will get the null out of it and if the key is present and has value . It will be stored as value.
2. When writing the getter for s3bucket (getS3Bucket), i dont want to write it for everykey value. Is there a automatic way to do this.
I looked at lot of posts but the scenario is not there.
P.S - I am new to java
I believe Jackson is exactly what you need. And if you are using Spring - it already uses Jackson under the hood I guess.
Here you can find some examples and documentation of how JSON mapping on to model class is done.
If you need to customize some behavior, you can use annotations like #JsonProperty (there are many).
If properties in your model class have the same names as properties in JSON, most probably you won't need to provide any further configs.
Here is a simple example:
public class User {
#JsonProperty("userName")
private String name;
private int age;
// getters and setters
}
And if you have JSON like this:
{
"userName" : "Foo Bar",
"age" : 18
}
Jackson will do all the magic for you unless you need something very specific.
If something is not in JSON you get (let's say you received JSON without age) - corresponding property in model class will be null if it is object type and default value (0, false, etc.) for primitives (in our case age would be 0).

Validate an Enum with springframework validation Errors

TL;DR : Enum deserialization errors are not caught by org.springframework.validation.Errors in a Rest Controller
For reference: we didn't find a clean solution yet as we finally decided that no one should call us wit a bad enum
I have a rest controller that uses org.springframework.validation.Errors for parameter validations:
#RequestMapping(value = "/vol1/frodo")
public ResponseEntity<Object> simpleMethodUsingPost(
HttpServletRequest httpServletRequest,
#Valid #RequestBody MySimpleObject simpleObject,
Errors errors) {
/* If an error occured, I need to log the object */
if (errors.hasErrors()) {
List<FieldError> fields = errors.getFieldErrors();
doSomething(fields , simpleObject);
}
}
My class MySimpleObject looks like this:
public class MySimpleObject {
#Valid
#NotNull(message = "anObjectField is a mandatory field")
private EmbeddedObject anObjectField = null;
#Valid
#NotNull(message = "aStringField is a mandatory field")
private String aStringField = null;
#Valid
private MySimpleEnum aSimpleEnum = null;
}
And my enum class MySimpleEnum is basically a class with two values:
public enum MySimpleEnum{
ORC("ORC"),
URUK("URUK");
private String value;
MySimpleEnum(String value) {
this.value = value;
}
#Override
public String toString() {
return String.valueOf(value);
}
}
The validation of this object (and the injection of errors in the springframework Error object) works well when it's on a String or an Object, but it will fail validation of an enum (hence an object containing a valid-annoted enum will fail too).
It fails when trying to cast the JSON String to an enum when the value is not valid:
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error:
Cannot deserialize value of type 'lotr.middleearth.model.MySimpleEnum' from String "HOBBIT"
This deserialization error is caught if I use a ResponseEntityExceptionHandler and override handleHttpMessageNotReadable, but then I don't have access to the different other parameters and can't use them.
How can I configure either a Validator, enum or springframework Error so that this exception is caught and usable in my controller body?
I just came across the same problem but didn't like the idea of giving the user an unformatted "ugly" validation error message.
First, I made the enum property not nullable on the pojo.
#NotNull(message = "Type must be NEW_APPLICATION or RENEWAL")
private RegistrationSubmissionTypeEnum type;
Then I changed the setter to basically check the input (as a string) and see if it matches one of the enums. If not, I do nothing, the property stays null and it's reported back as one of the validation error messages (using the message text used on the #NotNull annotation).
public void setType(Object typeInput) {
for (RegistrationSubmissionTypeEnum typeEnum : RegistrationSubmissionTypeEnum.values()) {
if (typeEnum.getKey().equalsIgnoreCase(typeInput.toString())) {
this.type=RegistrationSubmissionTypeEnum.valueOf(typeInput.toString());
}
}
}
That's really the key. The normal behavior we all despise generates an ugly error message, but it also does it in a way such that this error message is displayed alone. Personally, I like to send back all errors en masse.
I'm not a fan of hardcoding the enum values on the #NotNull message, but in this particular case (small number of enum values), it's preferable to the default enum serialization error message, and the behavior of a one-off isolated error message.
I considered a custom validator, but that started to feel heavy. Maybe someone can improve on this.
The problem that is occurring is that in the enum MySimpleEnum there is no constant "HOBBIT" the possibilities are "ORC" and "URUK", in the validation question can be used simply as in the example:
#NotNull(message = "Custom message")
private MySimpleEnum aSimpleEnum
I ended up doing something like that to extract the problematic field in the request :
int start = ex.getMessage().indexOf("[\"");
int end = ex.getMessage().indexOf("\"]");
String fieldName = exception.getMessage().substring(start + 2, end)
The field happens to be at the end of the message between the brackets.
I'm not really proud of that one, it's messy, but it seems to be the only way with enums.
I guess it would be better to use strings and proper Spring validation instead, since it depends too much on the implementation and may break with future updates.

Jackson fail deserialization if field is not present

I have a pojo like
class Pojo {
private String string;
public String getString() { return string; }
#JsonSetter(nulls = Nulls.FAIL)
public void setString(String string) {
this.string = string;
}
}
I want to make jackson fail when deserializing if the string field is null or absent. (i.e. {"string":null} or {})
As you can see, I've succeeded in the first goal with the JsonSetter annotation. What I am hoping for now is something like that but for a missing property. I found a few other questions asking similar things but they were quite old and referenced features that might be implemented in the future. With the recent release of jackson 2.9, I was hoping maybe this is now possible.
#JsonProperty has a required element that can be used
to ensure existence of property value in JSON
Unfortunately, Jackson currently (2.9) only supports it for use with #JsonCreator annotated constructors or factory methods. Since #JsonSetter only works with setters, you'll have to do the null validation yourself.
For example, you'd define a constructor like
#JsonCreator
public Pojo(#JsonProperty(value = "string", required = true) String string) {
if (string == null) {
throw new IllegalArgumentException("string cannot be null");
}
this.string = string;
}
If the property is present, but set to null, Jackson would throw an InvalidDefinitionException that wraps the IllegalArgumentException thrown in the constructor.
If the property is absent, Jackson would throw a MismatchedInputException stating that a property is missing.
Both of these exceptions are subtypes of JsonMappingException, so you can easily deal with them the same way.
With this solution, you could also get rid of the setter altogether and make the field final if that suited your design better.
You may perform bean validation here by annotating the field of interest with #NotNull.
You may remove the annotation from your setter.
class Pojo {
#NotNull
private String string;
public String getString() { return string; }
public void setString(String string) {
this.string = string;
}
}
Similarly if you want to fail the validation for other constraints like size, pattern etc, you may use similar equivalent annotations available here.

Using #NonNull for checking class members too

Let's say I have a class that has 3 members:
Class A {
private String string1; /** Cannot be null */
private String string2; /** Cannot be null */
private String string3; /** Can be null */
}
I have 2 method that accepts an object of this class as a parameter. One of the methods needs to check that the non nullable fields are present while in the other one, it doesn't matter:
public int func1(A object); /** Check non nullable fields */
public int func2(A object); /** No check required */
Is there any clean way to do it? Using #NonNull annotations or something?
I have tried various ways but none of them work. All the NonNull only help make sure that the setter doesn't get null as the parameter. Or that the object itself isn't null.
I can't seem to find something that does this kind of recursive null check on the object.
It'd be great if any of you could help. :)
You need a bean Validator, a class used to check that the bean is OK. In Spring there are a number of implementations. For example, see SmartValidatorand LocalValidatorFactoryBean
The #valid annotation is a nice way to call automagically the validator. As you are using Spring you can avoid the manual creation of the validator. It only works if the method is called by Spring (or any equivalent container). You may get the validation results in a BindingResult object. For example:
#RequestMapping(value = "/MyPath", method = RequestMethod.POST)
public String postCostForm(Model model, #Valid MyForm myForm, BindingResult result){
if(result.hasErrors()){
for(ObjectError error : result.getAllErrors()){
// Do something
}
return "/error";
}else{
return "/success";
}
}
Validation is very powerfull and sometimes complex. You may create groups of validation and check just one group of them. You can create your custom constraint tags, you can call validation methods and you may customize and internationalize the messages returned if the validation fails.
class A {
#NotNull
private String string1; /** Cannot be null */
#NotNull
private String string2; /** Cannot be null */
private String string3; /** Can be null */
}
And in the method signature have #Valid
public int function(#Valid A object)
Use #Required annotation in Spring's
private String string1;
#Required
public void setString1(String string1) {
this.string1= string1;
}

Categories

Resources