Problem Statement: I work in a Restful micro-service which is kind of a delegator or an orchestrator API on top of many other micro-services. Majority of the application logic lies in mapper classes that maps requests and responses to and from different dependent APIs. Having a clean and readable set of POJOs is a pre-requisite to have a clean, readable and maintainable set of mapper classes.
There are many instances wherein I had to de-serialize certain values coming from the dependent APIs to a Java enum. Normally when I want to deserialize a string, I use @JsonProperty annotation in my enum values to get it de-serialized to the enum.
For example, consider the below JSON payload which is a list of products. For some good reason, I had to declare productType as a enum in my POJO class. And Jackson’s @JsonProperty annotation helps me to de-serialize it.
[
{
"productName": "Savings",
"productType": "PT01"
},
{
"productName": "Loans",
"productType": "PT02"
}
]
Here is my POJO for an individual Product
@Data
public class Product {
private String productName;
private ProductType product;
}
@Getter
enum ProductType {
@JsonProperty("PT01")
PT01("PT01", "This is a savings product"),
@JsonProperty("PT02")
PT02("PT02", "This is a loans product"),
@JsonEnumDefaultValue
UNKNOWN("XX00", "This is a unknown product");
private String code;
private String description;
ProductType(String code, String description) {
this.code = code;
this.description = description;
}
}
But unfortunately, the above solution will only work when the incoming json value is a string. It won’t for integers and other data types.
For example, consider the below JSON payload
{
"name": "NoName",
"status": 101
}
The previous solution with @JsonProperty annotation will not work in this case because the data type of status is an integer.
Solution: I had to write a factory method with @JsonCreator annotation to fix this. And eere is my solution. If there are better alternatives, please feel free to add it in the comments.
@Data
class JsonPayload {
private String name;
private Status status;
}
enum Status {
CODE_101(101, "OK"),
CODE_102(102, "NOK"),
UNKNOWN(-1, "UNKNOWN");
private int code;
private String value;
Status(int code, String value) {
this.code = code;
this.value = value;
}
@JsonCreator
public static Status fromCode(Integer code){
return stream(values()).filter(e -> e.code == code)
.findFirst()
.orElse(UNKNOWN);
}
}
Here I have a factory method fromCode that iterates through all the values and returns the appropriate enum. The @JsonCreator annotation is a marker annotation that helps Jackson to identify the factory method to be used for instantiating the status object.
@JsonCreator
public static Status fromCode(Integer code){
return stream(values()).filter(e -> e.code == code)
.findFirst()
.orElse(UNKNOWN);
}
Checkout my github for my complete code. If there are better ways to do it, please comment it out in the comments section.