public interface Animal {}
public enum Dog implement Animal { DOG_1, DOG_2 }
public enum Cat implement Animal { CAT_1, CAT_2 }
@NoArgsConstructor
public class AnimalRequest {
private Animal animal;
}
Enum 클래스 Dog, Cat는 Animal이라는 인터페이스의 구현체이다.
Animal(interface)을 Dog 또는 Cat(Enum)으로 Deserialize 하기 위해서 Animal에 대한 JsonDeserializer를 커스텀해야 한다.
JsonDeserializer.java
public abstract class JsonDeserializer<T>
implements NullValueProvider // since 2.9
{
/*
/**********************************************************
/* Main deserialization methods
/**********************************************************
*/
/**
* Method that can be called to ask implementation to deserialize
* JSON content into the value type this serializer handles.
* Returned instance is to be constructed by method itself.
*<p>
* Pre-condition for this method is that the parser points to the
* first event that is part of value to deserializer (and which
* is never JSON 'null' literal, more on this below): for simple
* types it may be the only value; and for structured types the
* Object start marker or a FIELD_NAME.
* </p>
* <p>
* The two possible input conditions for structured types result
* from polymorphism via fields. In the ordinary case, Jackson
* calls this method when it has encountered an OBJECT_START,
* and the method implementation must advance to the next token to
* see the first field name. If the application configures
* polymorphism via a field, then the object looks like the following.
* <pre>
* {
* "@class": "class name",
* ...
* }
* </pre>
* Jackson consumes the two tokens (the <tt>@class</tt> field name
* and its value) in order to learn the class and select the deserializer.
* Thus, the stream is pointing to the FIELD_NAME for the first field
* after the @class. Thus, if you want your method to work correctly
* both with and without polymorphism, you must begin your method with:
* <pre>
* if (p.currentToken() == JsonToken.START_OBJECT) {
* p.nextToken();
* }
* </pre>
* This results in the stream pointing to the field name, so that
* the two conditions align.
* <p>
* Post-condition is that the parser will point to the last
* event that is part of deserialized value (or in case deserialization
* fails, event that was not recognized or usable, which may be
* the same event as the one it pointed to upon call).
*<p>
* Note that this method is never called for JSON null literal,
* and thus deserializers need (and should) not check for it.
*
* @param p Parsed used for reading JSON content
* @param ctxt Context that can be used to access information about
* this deserialization activity.
*
* @return Deserialized value
*/
public abstract T deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JacksonException;
StdDeserializer.java
/**
* Base class for common deserializers. Contains shared
* base functionality for dealing with primitive values, such
* as (re)parsing from String.
*/
public abstract class StdDeserializer<T>
extends JsonDeserializer<T>
implements java.io.Serializable,
ValueInstantiator.Gettable // since 2.12
{
private static final long serialVersionUID = 1L;
JsonDeserializer의 deserialize 메소드에서 요청 필드를 Animal 타입으로 변환해야 한다.
public class AnimalDeserializer extends StdDeserializer<Animal> {
protected AnimalDeserializer() {
super(Animal.class);
}
@Override
public Animal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
final String nameOfEnum = p.getText();
return Animal.valueOf(nameOfEnum);
}
}
StdDeserializer의 구현 클래스를 private Animal animal;
필드의 deserializer로써 등록해주자.
@NoArgsConstructor
public class AnimalRequest {
@JsonDeserialize(using = AnimalDeserializer.class)
private Animal animal;
}