简体   繁体   中英

Problem with Deserialization of Generic Classes

I came across a problem with Generics and Jackson recently and ended up with not using it. I have an interface M.netaryType :

public interface MonetaryType implements Serializable {}

which is implemented by multiple enum s like:

public enum IncomeType implements MonetaryType {
    FULL_TIME_SALARY,
    PART_TIME_SALARY,
    CHILD_BENEFIT
}

public enum ExpenseType implements MonetaryType {
    HEAT,
    CONDO_FEES,
    ELECTRICITY
}

I created a Generic Class:

public MonetaryValidation<T extends MonetaryType> {
    
   private T monetaryType;
   private boolean isPassed;
   private String message;

   // Getters and Setters

}

This object is not deserialized by Jackson library. Meaning that if any Spring REST endpoints are called while the payload contains M.netaryValidation object, it throws below exception:

java.lang.IllegalArgumentException: Cannot construct instance of **.enumeration.M.netaryType (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information

I do not want to solve the issue with Jackson polymorphic deserialization approach since it requires the client to pass an extra flag specifying the concrete implementation of the interface or abstract class, as far as I understood.

Unfortunately I ended up creating multiple sub classes of non-generic M.netaryValidation (one subclass per each M.netaryType subclass), which I know it is not a decent solution.

It is much appreciated if you could help me out to understand where the problem is and whether there is an approach to use @JsonSubTypes while passing an extra field is not needed.

There is an idea, try accept m.netaryType as the String type parameter, and you can custom converter in the Generic class for handling the generic type field, such as:

        public void setMonetaryType(String monetaryType) {
            Optional<IncomeType> incomeType = Arrays.stream(IncomeType.values()).filter(i -> i.name().equals(monetaryType)).findFirst();

            incomeType.ifPresent(i -> {
                this.monetaryType = (T)i;
            });

            Optional<ExpenseType> expenseType = Arrays.stream(ExpenseType.values()).filter(i -> i.name().equals(monetaryType)).findFirst();

            expenseType.ifPresent(i -> {
                this.monetaryType = (T)i;
            });
        }

I think this is a simply way to achieve other than using JsonSubTypes or custom Converters, since it's really a generic parameter.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM