Our REST API receives some JSON objects input where some fields are required to be not null. Those can be either String/Integer or even might be some other class instance as reference.
We are trying to find a way to enforce those field to be not null, instead of the correct way for null check in the API. Current:
if (myObject.getSomeOtherObject() == null)
throw new SomeException();
What we want to have is something like:
class MyObject{
@Required
OtherObject someOtherObject;
// ...
}
We have tried 3 things:
1) Upgrade to jackson 2.0.6 and use annotation com.fasterxml.jackson.annotation.JsonProperty But, this just looks not working. Found those references: http://jira.codehaus.org/browse/JACKSON-767
2) Extending JsonDeserializer to check null but the problem is that it does not even executed on the null input.
public class NotNullDeserializer<T> extends JsonDeserializer<T> {
@Override
public T deserialize(JsonParser jsonparser, DeserializationContext deserializationcontext) throws IOException, JsonProcessingException {
ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> type = (Class<T>) superClass.getActualTypeArguments()[0];
T t = jsonparser.readValueAs(type);
if (t == null){
String classNameField = type.getName();
String field = jsonparser.getCurrentName();
throw new WrongInputException("The field '"+field+"' of type '"+classNameField+"' should not be null.");
}
return t;
}
}
public class NotNullAddressDeserializer extends NotNullDeserializer<Address> {
}
@JsonDeserialize(using=NotNullAddressDeserializer.class)
Address to;
3) Writing our own @Required annotation and trying to check with ResourceFilter, but it seems I cannot get the actual object from the ContainerRequest and even if we could, not sure how to execute deep check of null reference in object.otherObject.someObject.fieldNotNullable
private class Filter implements ResourceFilter, ContainerRequestFilter {
private final ArrayList<String> requiredParameters;
protected Filter() {
requiredParameters = null;
}
protected Filter(ArrayList<String> requiredParameters) {
this.requiredParameters = requiredParameters;
}
@Override
public ContainerRequestFilter getRequestFilter() {
return this;
}
@Override
public ContainerResponseFilter getResponseFilter() {
return null;
}
@Override
public ContainerRequest filter(ContainerRequest request) {
if (requiredParameters != null && requiredParameters.size() > 0) {
MultivaluedMap<String, String> params = request.getQueryParameters();
params.putAll(request.getFormParameters());
StringBuffer missingParams = new StringBuffer();
for (String reqParam : requiredParameters) {
List<String> paramValues = params.get(reqParam);
if (paramValues == null || paramValues != null && paramValues.size() == 0)
missingParams.append(reqParam + ",");
}
if (missingParams.length() > 0)
throw new WrongInputException("Required parameters are missing: " + missingParams);
}
return request;
}
}
JAX-RS separates quite nicely the deserialization from the validation, i.e. Jackson has by design no mechanism to enforce values to be non-null, etc. Instead, you can use BeanValidation for that:
- Add a dependency to
javax.validation:validation-apiinprovidedscope. - Add the
javax.validation.constraints.NotNullannotation to your field.
For more details, go here.
You can use JSON-SCHEMA as you can express many constraints on JSON fields with it: http://json-schema.org/
Then you can generate from the schema your java classes with @NotNull JSR 303 annotations and use bean validation on your object. It works with Jackson natively, so you should not have any problem.
For instance, you can use the maven plugin to do so: http://wiki.jsonschema2pojo.googlecode.com/git/site/0.3.7/generate-mojo.html
@Required is a Spring framework annotation for injected beans, so I'd say don't use it for this purpose.
You can use this one instead:
http://robaustin.wikidot.com/annotations-and-notnull
@NotNull
String myString;
For runtime checks, try http://code.google.com/p/notnullcheckweaver/
You can enforce not null validation using a combination of the Jackson JSON library and the javax.validation together with the Hibernate validator package.
If you are using Maven these are the dependencies you can use:
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson-version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson-version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.6</version>
</dependency>
</dependencies>
Then your code will have to convert some JSON into your annotated object and you will need to validate the object using javax.validation.Validator. Here is some sample code demonstrating how this can be done (see the relevant validate method):
public class ShareLocationService {
private ObjectMapper mapper = new ObjectMapper();
private ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
// Materialize the Java object which contains the validation annotations
public ShareLocation readFrom(String json) throws IOException {
return mapper.readerFor(ShareLocation.class).readValue(json);
}
// validate and collect the set of validations
public Set<String> validate(String json) throws IOException {
ShareLocation shareMyLocation = readFrom(json);
Validator validator = factory.getValidator();
Set<ConstraintViolation<ShareLocation>> violations = validator.validate(shareMyLocation);
return violations.stream().map(Object::toString).collect(Collectors.toSet());
}
}
Here is a sample class using the validation annotations:
public class ShareLocation {
@JsonProperty("Source")
@NotNull
private String source;
@JsonProperty("CompanyCode")
private String companyCode;
@JsonProperty("FirstName")
private String firstName;
@JsonProperty("LastName")
private String lastName;
@JsonProperty("Email")
private String email;
@JsonProperty("MobileNumber")
private String mobileNumber;
@JsonProperty("Latitude")
@NotNull
private Double latitude;
@JsonProperty("Longitude")
@NotNull
private Double longitude;
@JsonProperty("LocationDateTime")
@NotNull
private String locationDateTime;
来源:https://stackoverflow.com/questions/12813319/enforce-not-null-field-in-json-object