It is a useful habit to restrict the input of your REST interface as much as possible. Therefore I use java enumeration classes as field type for REST input classes, whenever it is possible.
It also has the advantage, that you can document the possible values of a given field. It makes impossible to the client to send invalid values, without needing to validate them in your code. In case of invalid value is being sent, the client gets error response form the Spring REST frameworks itself.
In most of the cases the serialization and deserialization works out of the box with Spring, but you can face cases, when you need to write your own classes to do the job. Recently I faced the problem, that I needed to allow a value, that started with a digit, like “3Test”. The problem is, that in Java, you are not allowed to define an enum value, starting with numeric character. Therefore I defined the enum like this:
public enum Application { THREETEST("3TEST"), FOURTEST("4TEST"); private final String appName; private Application(String cabAppName) { this.appName = cabAppName; } public String getAppName() { return appName; } }
In the Json REST input the values “3TEST” and “4TEST” are allowed to use.
In order to let Spring to convert the Json input to the corresponding input class, we need to define, how the conversion to Application should work. Therefore, we need a JsonDeserializer implementation.
import java.io.IOException; import java.util.Locale; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; public class ApplicationEnumDeserialize extends JsonDeserializer { @Override public Application deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); String applicationAsText = node.asText().toUpperCase(Locale.ENGLISH); for (int i = 0; i < Application.values().length; i++) { Application app = Application.values()[i]; if (app.getAppName().equals(applicationAsText)) { return app; } } throw new InboundMessageValidationException("Invalid os defined: " + applicationAsText); } }
Normally, you do not need this class, as you use the REST request class as input for your service. But in order to use it in integration test, I needed to implement the opposite way, also serializing the enum to Json. Therefore I implemented a JsonSerializer class.
import java.io.IOException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; public class ApplicationEnumSerializer extends JsonSerializer { @Override public void serialize(Application value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { gen.writeString(value.getAppName()); } }
In order to let Spring use these classes, you need to define them in the REST input class, using the @JsonDeserialize and @JsonSerialize annotations respectively.
import java.io.Serializable; import javax.validation.constraints.NotNull; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.NoArgsConstructor; import lombok.Value; @Value @Builder @AllArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(force = true) public class RegistrationRequest implements Serializable { @NotNull @JsonProperty("appName") @JsonDeserialize(using = ApplicationEnumDeserialize.class) @JsonSerialize(using = ApplicationEnumSerializer.class) private Application application; ... }