I am deserializing a JSON into a model with Jackson. Say I have the following model:
class Model {
private String fieldA;
private Optional<String> fieldB;
}
Here, fieldA is non-nullable. In the JSON, it can either be absent or filled in:
{
}
or
{
"fieldA": "value"
}
In Java, the not filled in-case results in a null value of the member fieldA.
My question: is there a way to reject a null value for fieldA? For the following input JSON, Jackson should throw an exception:
{
"fieldA": null
}
Note that I only want that behaviour for fieldA, and not fieldB, since fieldB is nullable (and hence it is wrapped around an Optional in the model).
I could also wrap fieldA in an Optional and add some bean validation there, but I would rather not change the model.
No Jackson does not provide validation API. You can just ignore null value by including #JsonInclude(Include.NON_NULL) to the class or use Bean Validation API which will validate and throw errors if conditions are not satisfied.
UPDATE:
For your comment answer, if anyhow you just wanted to skip fieldA value if it is null and let other allowed them than in setter method you could just manually check.
In your case:
public void setFieldA(String fieldA) {
if (fieldA != null) {
this.fieldA = fieldA;
}
}
Related
I've created the annotation I want to put on some fields of a class.
I want the annotation to check one of two or more fields:
#Documented
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface OneOfFields {
}
For example - the class:
public class MyClassRq {
#OneOfFields
private String stringOne;
#OneOfFields
private String stringTwo;
}
If I create an HttpRequest with the body and set both fields, I want to get an exception, javax.validation exception is also possible.
What is the best way to write the validator?
Annotations can be processed in two phases:
At compile time (in this case through an Annotation Processor)
At runtime (in this case through reflection)
It depends on when you want to perform the check. Considering that it seems you want to check this at runtime (i.e. when you receive the object), then you could create a sample method that takes an object, scans all the fields of the object for the annotation #OneOfFields and if more than one is not null, then it throws an exception:
public static <T> T validate(T input) {
try {
int numberOfAnnotatedNonNull = 0;
for (Field field : input.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(OneOfFields.class) && (field.get(input) != null)) {
numberOfAnnotatedNonNull++;
if (numberOfAnnotatedNonNull > 1) {
throw new IllegalStateException("More than one field annotated by #OneOfFields has been set for this object");
}
}
}
return input;
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not validate input object", e);
}
}
Sample usage:
MyClassRq myClassRq = validate(yourInput);
If the above yourInput of type MyClassRq is valid, then it will simply return the same object. Else, it will throw an exception.
Some notes:
Here I'm throwing as soon as I find more than one field which is non null. You may of course create a cleaner error message (for example by collecting all the fields which are illegally set and returning their names)
Here I'm throwing a standard IllegalStateException but you can (you should probably) create your own custom exception
Don't forget to check that T input is not null (if it is, my code will crash).
This is a sample usage of the standard Java Reflect API, there are several ways of reaching the same purpose (I've just shown you the most "readable")
This question already has answers here:
How to distinguish between null and not provided values for partial updates in Spring Rest Controller
(8 answers)
Closed 2 years ago.
I have a PATCH REST endpoint, exposing a JSON interface, which can be used to partially update an entity, based on the attributes which are actually sent in the body.
Let's consider a sample class representing the entity:
class Account {
private UUID accountUuid;
private UUID ownerUuid;
private String number;
private String alias;
// constructor, getters and setters omitted
}
where accountUuid, ownerUuid and number are required properties, and alias is optional. Additionally I have a command class for updating said account:
class UpdateAccountCommand {
private UUID accountUuid;
private String number;
private String alias;
// constructor, getters and setters omitted
}
For my PATCH endpoint, e.g. PATCH /accounts/{account-uuid}, I'd like to implement a functionality, that only properties actually sent in the request are changed:
// this should only update the account number
{"number": "123456"}
// this should only update the alias, by setting it to null
{"alias": null}
// this shouldn't update anything
{}
For required properties, this is fairly easy to do. After deserialisation from the JSON string to UpdateAccountCommand instance using Jackson, I simply check if a required property is null, and when it's not null, I update the given property.
However the situation complicates with optional properties, since the alias property is null in both cases:
when the request body explicitly specifies the alias as null,
when the alias property is not specified in the request body at all.
How can I model these optional properties, so that I can indicate this removable mechanism?
A naive solution would be to introduce some sort of a wrapper, which would not only contain the raw value (e.g. for the alias: string property), but also a boolean, indicating whether a property was specified in the body or not. This would require you to write a custom deserialisation mechanism, which can be a tedious work.
Since the question is about Java 8, for Java 8 and newer, I recommend using a nullable Optional, which works pretty much out of the box with Jackson.
For optional (removable fields), you change the raw values by wrapping them in optional:
class UpdateAccountCommand {
private UUID accountUuid;
private String number;
private Optional<String> alias;
// constructor, getters and setters omitted
}
In order for Jackson to work with Optional<*> fields correctly, the Jdk8Module module has to be registered to the object mapper, e.g.:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
The following code:
public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
String withNewAliasS = "{\"alias\":\"New Alias\"}";
String withNullAliasS = "{\"alias\":null}";
String withoutPropertyS = "{}";
UpdateAccountCommand withNewAlias = objectMapper.readValue(withNewAliasS, UpdateAccountCommand.class);
if (withNewAlias.getAlias() != null && withNewAlias.getAlias().isPresent()) {
System.out.println("withNewAlias specified new alias.");
}
UpdateAccountCommand withNullAlias = objectMapper.readValue(withNullAliasS, UpdateAccountCommand.class);
if (withNullAlias.getAlias() != null && !withNullAlias.getAlias().isPresent()) {
System.out.println("withNullAlias will remove an alias.");
}
UpdateAccountCommand withoutProperty = objectMapper.readValue(withoutPropertyS, UpdateAccountCommand.class);
if (withoutProperty.getAlias() == null) {
System.out.println("withoutProperty did not contain alias property on input at all.");
}
}
then prints out this to the console:
withNewAlias specified new alias.
withNullAlias will remove an alias.
withoutProperty did not contain alias property on input at all.
you can add an additional boolean property which says if the optional property was present in request
class UpdateAccountCommand {
//...
private String alias;
#JsonIgnore
private boolean isAliasSet;
#JsonProperty
public void setAlias(String value) {
this.alias = value;
this.isAliasSet = true;
}
}
the setter is called only when "alias" is present, be it null or with value
Since upgrading my webapplication from Spring 3.0.5 to 3.1.1 I have to face some serious errors when validating my form-beans. My previously configured validator(s) doesn't work any more like they should. The problem is that the method getFieldValue(String fieldname) from Class org.springframework.validation.Errors does not return the original binded bean value like it should (and was before).
This is what my form-bean looks like:
public class PersonBean extends BaseFormBean {
private String firstname; // getters and setter omitted...
private String lastname; // getters and setter omitted...
private Integer age; // getters and setter omitted...
public PersonBean() {}
#Override
public void validateForm(Errors errors) {
WebValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstname", "validator.requiredvalidator.lbl", "field required");
WebValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastname", "validator.requiredvalidator.lbl", "field required");
WebValidationUtils.rejectInvalidIntValue(errors, "age", "validator.std.age", "invalid age", false);
}
}
The WebValidationUtils-class that gets invoked has some standard methods for checking bean properties. The error occurcs only on non-String values, like the property age which is of type Integer. It also happens on Collection(s).
The following snippet shows how Integer values are validated in my utils-class:
import org.springframework.validation.Errors;
...
public abstract class WebValidationUtils {
...
public static void rejectInvalidIntValue(Errors errors, String field, String errorCode, String defaultMessage){
Assert.notNull(errors, "Errors object must not be null");
Object value = errors.getFieldValue(field); // returns the string value (type: java.lang.String)
Class<?> fieldType = errors.getFieldType(field); // returns the class Integer!
if (value == null || !value.getClass().equals(Integer.class) || ((Integer)value).intValue() <= 0){
errors.rejectValue(field, errorCode, null, defaultMessage);
}
}
}
The bean itself has the correct value bound...
Do I have to configure some additonal spring beans in my context-servlet.xml do achieve the same bevahior like it was in 3.0.5?
Edit: The official Spring Doku for the method "getFieldValue(...)" says
Return the current value of the given field, either the current bean property value or a rejected update from the last binding.
So I don't have a clue why this method returns a String-value instead of the propagated bean value of type Integer...
Seem like you have a binding error so getFieldValue() return FieldError.getFieldValue() that return the value that causes the binding error. This is the expected behavior.
You can get the value that hold the property using getRawFieldValue() instead. This method always return the value using the PropertyAccessor.
In my datamodel a have many entities where attributes are mapped to enumerations like this:
#Enumerated(EnumType.STRING)
private MySpecialEnum enumValue;
MySpecialEnum defines some fixed values. The mapping works fine and if the database holds a NULL-value for a column I get NULL in the enumValue-attribute too.
The problem is, that my backend module (where I have no influence on) uses spaces in CHAR-columns to identify that no value is set. So I get an IllegalArgumentException instead of a NULL-value.
So my question is: Is there a JPA-Event where I can change the value read from the database before mapping to the enum-attribute?
For the write-access there is the #PrePersist where I can change Null-values to spaces. I know there is the #PostLoad-event, but this is handled after mapping.
Btw: I am using OpenJpa shipped within WebSphere Application Server.
You could map the enum-type field as #Transient (it will not be persisted) and map another field directly as String, synchronizing them in #PostLoad:
#Transient
private MyEnum fieldProxy;
private String fieldDB;
#PostLoad
public void postLoad() {
if (" ".equals(fieldDB))
fieldProxy = null;
else
fieldProxy = MyEnum.valueOf(fieldDB);
}
Use get/setFieldProxy() in your Java code.
As for synchronizing the other way, I'd do it in a setter, not in a #PreUpdate, as changes to #Transient fields probably do not mark the entity as modified and the update operation might not be triggered (I'm not sure of this):
public void setFieldProxy(MyEnum value) {
fieldProxy = value;
if (fieldProxy == null)
fieldDB = " ";
else
fieldDB = value.name();
}
OpenJPA offers #Externalizer and #Factory to handle "special" database values.
See this: http://ci.apache.org/projects/openjpa/2.0.x/manual/manual.html#ref_guide_pc_extern_values
You might end up with something like this: not tested...
#Factory("MyClass.mySpecialEnumFactory")
private MySpecialEnum special;
...
public static MySpecialEnum mySpecialEnumFactory(String external) {
if(StringUtils.isBlank(external) return null; // or why not MySpecialEnum.NONE;
return MySpecialEnum.valueOf(external);
}
What happens if I annotate a constructor parameter using #JsonProperty but the Json doesn't specify that property. What value does the constructor get?
How do I differentiate between a property having a null value versus a property that is not present in the JSON?
Summarizing excellent answers by Programmer Bruce and StaxMan:
Missing properties referenced by the constructor are assigned a default value as defined by Java.
You can use setter methods to differentiate between properties that are implicitly or explicitly set. Setter methods are only invoked for properties with explicit values. Setter methods can keep track of whether a property was explicitly set using a boolean flag (e.g. isValueSet).
What happens if I annotate a constructor parameter using #JsonProperty but the Json doesn't specify that property. What value does the constructor get?
For questions such as this, I like to just write a sample program and see what happens.
Following is such a sample program.
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
// {"name":"Fred","id":42}
String jsonInput1 = "{\"name\":\"Fred\",\"id\":42}";
Bar bar1 = mapper.readValue(jsonInput1, Bar.class);
System.out.println(bar1);
// output:
// Bar: name=Fred, id=42
// {"name":"James"}
String jsonInput2 = "{\"name\":\"James\"}";
Bar bar2 = mapper.readValue(jsonInput2, Bar.class);
System.out.println(bar2);
// output:
// Bar: name=James, id=0
// {"id":7}
String jsonInput3 = "{\"id\":7}";
Bar bar3 = mapper.readValue(jsonInput3, Bar.class);
System.out.println(bar3);
// output:
// Bar: name=null, id=7
}
}
class Bar
{
private String name = "BLANK";
private int id = -1;
Bar(#JsonProperty("name") String n, #JsonProperty("id") int i)
{
name = n;
id = i;
}
#Override
public String toString()
{
return String.format("Bar: name=%s, id=%d", name, id);
}
}
The result is that the constructor is passed the default value for the data type.
How do I differentiate between a property having a null value versus a property that is not present in the JSON?
One simple approach would be to check for a default value post deserialization processing, since if the element were present in the JSON but had a null value, then the null value would be used to replace any default value given the corresponding Java field. For example:
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFooToo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY);
// {"name":null,"id":99}
String jsonInput1 = "{\"name\":null,\"id\":99}";
BarToo barToo1 = mapper.readValue(jsonInput1, BarToo.class);
System.out.println(barToo1);
// output:
// BarToo: name=null, id=99
// {"id":99}
String jsonInput2 = "{\"id\":99}";
BarToo barToo2 = mapper.readValue(jsonInput2, BarToo.class);
System.out.println(barToo2);
// output:
// BarToo: name=BLANK, id=99
// Interrogate barToo1 and barToo2 for
// the current value of the name field.
// If it's null, then it was null in the JSON.
// If it's BLANK, then it was missing in the JSON.
}
}
class BarToo
{
String name = "BLANK";
int id = -1;
#Override
public String toString()
{
return String.format("BarToo: name=%s, id=%d", name, id);
}
}
Another approach would be to implement a custom deserializer that checks for the required JSON elements. And yet another approach would be to log an enhancement request with the Jackson project at http://jira.codehaus.org/browse/JACKSON
In addition to constructor behavior explained in #Programmer_Bruce's answer, one way to differentiate between null value and missing value is to define a setter: setter is only called with explicit null value.
Custom setter can then set a private boolean flag ("isValueSet" or whatever) if you want to keep track of values set.
Setters have precedence over fields, in case both field and setter exist, so you can "override" behavior this way as well.
I'm thinking of using something in the style of an Option class, where a Nothing object would tell me if there is such a value or not. Has anyone done something like this with Jackson (in Java, not Scala, et al)?
(My answer might be useful to some people finding this thread via google, even if it doesn't answer OPs question)
If you are dealing with primitive types which are omittable, and you do not want to use a setter like described in the other answers (for example if you want your field to be final), you can use box objects:
public class Foo {
private final int number;
public Foo(#JsonProperty Integer number) {
if (number == null) {
this.number = 42; // some default value
} else {
this.number = number;
}
}
}
this doesn't work if the JSON actually contains null, but it can be sufficient if you know it will only contain primitives or be absent
another option is to validate the object after deserialization either manually or via frameworks such java bean validation or, if you are using spring, the spring validation support.