简体   繁体   中英

Java - Generic abstract class for Enum Converter

I have some enums which are name("String") and value("Integer") pair, current in each enum I have to implement a @JsonCreator method for Json Deserialize which the input could be either name or value

//User Enum
public enum UserType {
   StakeHolder(1),...

   private static final Map<String, UserType> helper = ImmutableMap.<String, UserType>builder()
            .put(String.valueOf(StakeHolder.getValue()), StakeHolder)
            .put(StakeHolder.name(), StakeHolder)
            .build();

   @JsonCreator
   public static UserType jsonDeserialize(String json) {
        return helper.get(json);
   }
}

//Role Enum
public enum RoleType {
   Admin(1),...

   private static final Map<String, RoleType> helper = ImmutableMap.<String, RoleType>builder()
            .put(String.valueOf(Admin.getValue()), Admin)
            .put(Admin.name(), Admin)
            .build();

   @JsonCreator
   public static RoleType jsonDeserialize(String json) {
        return helper.get(json);
   }
}

but I was wondering if there is any way to create an abstract class that can have the implementation overall enums, that's to avoid similar "duplicate" code implemented in each of my enum always. Current code would be like

public abstract class JsonDeserializeEnum<E extends Enum<E>> {
  // protected Enum<E> myEnums;
  // private static final Map<String, E> myEnumHelper; ???

  public JsonDeserializeEnum() {
    super();
    // how to loop through myEnums to do something like
    // for(E iterator : myEnums.value()) not work...myEnums is not a collection of enums?
    // myEnumHelper.put(String.valueOf(iterator.getValue()), (E) iterator);
    // myEnumHelper.put(iterator.name, (E) iterator);
  } 

  @JsonCreator
   public static E jsonDeserialize(String json) {
        return myEnumHelper.get(json);
   }
}

//then
public enum UserType implements JsonDeserializeEnum<UserType>{
    StakeHolder(1),...
}
public enum RoleType implements JsonDeserializeEnum<RoleType>{
    Admin(1),...
}

I am stuck at two challenges here,

1) I would like to have an init/constructor in my abstract class that can load all available Enums into a static Map(only once), for example,

when calling UserType , load all user type enum into the static map, then deserialize from that map; when calling RoleType , load all role type enum into map, then deserialize from same map

2) If No.1 is not possible, it's Ok, Let's say if I will get rid of the map in my abstract class, but still use map defined in each of my Enum, is there anyway that my abstract class know and can use the map defined in my Enum?

The key is to use a JsonDeserializer . First you need to use an interface to be able to use the same code for all your enums:

public interface ValueEnum {
    String name();
    int value();
}

And implement it in every of the enums

public enum EnumExample implements ValueEnum {

    Hello(1),
    World(2);

    public final int value;

    private EnumExample(int value) {
        this.value = value;
    }

    @Override
    public int value() {
        return value;
    }
}

(The name() is already implemented by the Enum)

Then you can move your map creation to a separate class (I use the java HashMap, you can use whatever you like)

public class ValueEnumDeserializer<T extends ValueEnum> extends JsonDeserializer<T> {

    private final HashMap<String, T> map;

    public ValueEnumDeserializer(Class<T> c) {
        map = new HashMap<>();
        for (final T i : c.getEnumConstants()) {
            map.put(i.name(), i);
            map.put(String.valueOf(i.value()), i);
        }
    }

    @Override
    public T deserialize(JsonParser p, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        return map.get(p.getText());
    }
}

And then when you create your object mapper you must pass the deserializer

    final ObjectMapper mapper = new ObjectMapper();
    final SimpleModule module = new SimpleModule();
    module.addDeserializer(EnumExample1.class, new ValueEnumDeserializer<>(EnumExample1.class));
    module.addDeserializer(EnumExample2.class, new ValueEnumDeserializer<>(EnumExample2.class));
    module.addDeserializer(EnumExample3.class, new ValueEnumDeserializer<>(EnumExample3.class));
    mapper.registerModule(module);

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