Key-value map of each value of an enum in Java

beegotsy :

Task

In a Java back-end project, I have some (100+) external enums that I can't edit and I need to output them to our front-end. I wanted to output them in a JSON-object like manner. Each enum has different properties name.

e.g. for the following enum

public enum Colors {
  RED(1, "RED", "ff0000", Boolean.TRUE),
  GREEN(2, "GREEN", "00ff00", Boolean.FALSE),
  BLUE(3, "BLUE", "0000ff", Boolean.TRUE);

  private int code;
  private String label;
  private String hexCode;
  private boolean isAwesome;

  // ... getters and other methods
}

i want to output

[
  {
    label: "RED"
    hexCode: "ff0000"
    isAwesome: true
  },
  {
    label: "GREEN"
    hexCode: "00ff00"
    isAwesome: false
  },
  ...
]

My attempt

I am new to Java, this is the first time I used reflection and I didn't really study anything before going into this. Probably there are some major problems with this code (like performance or some other weird stuff that I don't know), but it compiles and does the job. I don't know if this is safe, so I ask if there are some better ways to do this.

private <T> List<HashMap<String, Object>> enumInserter(Class<T> clazz, List<String> properties) {
    return valuesToMap(clazz.getEnumConstants(), parserFactory(clazz, properties));
}

/**
* 
* @param <T>    type of the enum class
* @param values enumConstants of the enum
* @param parser a function that take a single enumValue of type <T> and returns
*               an property-value map
* @return the array of the property-value maps of each value
*/
private <T> List<HashMap<String, Object>> valuesToMap(T[] values, Function<T, HashMap<String, Object>> parser) {
  List<HashMap<String, Object>> enumValues = new ArrayList<>();
  for (T enumValue : values) {
    HashMap<String, Object> processedValue = parser.apply(enumValue);
    enumValues.add(processedValue);
  }
  return enumValues;
}

/**
* 
* @param <T>        the type of the enum class
* @param clazz      the enum class
* @param properties the properties to be added in the map
* @return a parser function that take a single enumValue of type <T> as input and
*         returns a property-value map of the given enumValue
*/
private <T> Function<T, HashMap<String, Object>> parserFactory(Class<T> clazz, List<String> properties) {
  return ((T enumValue) -> {
    HashMap<String, Object> map = new HashMap<>();

    properties.stream().forEach(propertyName -> {
      String methodName = getterFromProperty(propertyName);
      try {
        Method method = clazz.getMethod(methodName);
        Object methodResult = method.invoke(enumValue);
        map.put(propertyName, methodResult);
      } catch (Exception e) {
        // ... error logging
      }
    });

    return map;
  });
}

/**
* Return the "standard" property getter of a property. e.g. "example" will
* return "getExample"
* 
* @param property
* @return property getter method name
*/
private String getterFromProperty(String property) {
  return "get" + property.substring(0, 1).toUpperCase() + property.substring(1);
}
Alexandar Petrov :

The usual approach to this is either through use of annotation @JsonFormat(shape = JsonFormat.Shape.OBJECT) or through usage of a custom tailored serializer.

Lets say that you are referencing the enum from class A

class A {

   @JsonFormat(shape = JsonFormat.Shape.OBJECT)
   private Colors color;

}

this will make the color to be serialized the way you want.

Alternative aproach would to register a custom serializer for your Enum this you can do the following way:

public class ColorSerializer extends StdSerializer {

    public ColorSerializer() {
        super(Color.class);
    }

    public ColorSerializer(Class t) {
        super(t);
    }

    public void serialize(Color color, JsonGenerator generator,
      SerializerProvider provider) 
      throws IOException, JsonProcessingException {
        generator.writeStartObject();
        generator.writeFieldName("code");
        generator.writeString(color.getCode());
        generator.writeFieldName("hexCode");
        generator.writeString(color.getHexcode());
        generator.writeFieldName("isAwsome");
        generator.writeNumber(color.isAwsome());
        generator.writeEndObject();
    }
}

Since your enums are external you can always place them in wrappers which are internal to your project and this way control their serialization process.

If you want to serialize them using the same strategy you can place your reflection code in the serializer. This way you will get a single generic serializer, instead of writing for each enum.

This is how you can register the custom serializer:

ObjectMapper mapper = new ObjectMapper();

SimpleModule module = new SimpleModule();
module.addSerializer(Color.class, new ColorSerializer ());
mapper.registerModule(module);

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=82884&siteId=1