Java annotation: validating primitives inside a list via an annotation

1.9k views Asked by At

I would like to be able to validate the primitives inside a list and I was wondering if there's a standard way of doing it. For example validating the size of the Strings inside a list.

An example:

@Size(max = 64)  // validates that a string is max 64 chars.
private String name;

@Size(max = 64)  // validates that the list size is max 64 items.
private List<String> names;

// What I want:
@ValidateInsidePrimitives(
   @Size(max = 64)
)
private List<String> names;

What I've seen people do is wrap the primitives into an object do do this:

@Valid
private List<NameObj> names;

OR making custom annotations that validate a list of strings:

@MyCustomListStringSizeAnnotation(max = 64) 
private List<String> names;

OR using the java 8 new annotations placement like so:

private List<@Size(max = 64) String> names;

But I don't really like the first 2 approaches, and I CANNOT port my app to java 8.

It seems to me that the "@ValidateInsidePrimitives" I drafted above should work (after all the *.List annotations do take annotations) but I wasn't able to find something like that. Any ideas?

2

There are 2 answers

0
mernst On

You can take advantage of Java 8 type annotations even if you cannot port your app to Java 8.

In particular, you can write a pluggable type-checker using the Checker Framework. Then, in your code you write the annotations in comments, like this:

List</*@Size(max=64)*/ String> 

The Checker Framework processes annotations in comments, just as if you had written them without the comments. However, an ordinary Java compiler just sees and ignores the comments, so your code continues to compile with any Java compiler.

At compile time, the Checker Framework runs your pluggable type-checker and enforces the semantics that you specified.

Note that the Checker Framework does type-checking to give you a compile-time guarantee about what values will ever be put in your list. It does not add run-time asserts to your code so that your code will raise an error if an undesired value is put in your list. If you want run-time checking, you will need to extend the Checker Framework or to use a different tool.

0
Gunnar On

The problem with @ValidateInsidePrimitives is that it cannot be implemented in a generic manner.

Annotation members (such as value()) can only have a specific other annotation type as type, but not java.lang.annotation.Annotation (that's the reason why there is a dedicated @List constraint for each BV built-in constraint). So you could implement @ValidateInsidePrimitives for @Size, but would have to do so for each other basic constraint to be supported.

You are better off implementing the custom @MyCustomListStringSizeAnnotation constraint if you cannot migrate to Java 8 yet.