엄범

 

  • EnumConstant 하나를 `` {fieldName:fieldValue}`` 형식으로 매핑하고 싶은 경우가 있다 ( 주로 뷰로 전달해야 할 때. )
  • 기본적으로 jackson의 ObjectMapper는 serialize/deserialize 할 때 Enum 코드만 내려주도록 되어 있다.
    • 즉, ``java PaymentCode.CARD``를 변환하면 ``java "CARD"``가 된다.
    • ``java {code=1, koName="카드", enName="card"}`` 형태로 내려주려면, 뭔가 해줘야 한다

 

방법 1 ) Jackson / ObjectMapper를 사용하고 싶은 경우

 

방법 1-1 ) @JsonFormat.Shape.OBJECT
  • key:value 쌍을 내려주고 싶은 경우를 대비해 jackson은 ``java JsonFormat.Shape.OBJECT`` 를 제공하고 있다.
  • https://fasterxml.github.io/jackson-annotations/javadoc/2.7/  
    • 아래와 같이 지정해주면 알아서 serialize 시 key=value 형식으로 매핑해준다.
    • deserialize 시에는 그냥 CODE만 받아도 enum으로 받아준다. (controller에서 받을 때 등등)

```java

@JsonFormat(shape = JsonFormat.Shape.OBJECT)

public enum EnumCls

```

 

  • 반면, 붙어 있는 애너테이션을 무시하고 싶다면? 

```java

objectMapper.disable(MapperFeature.USE_ANNOTATIONS);

```

 

방법 1-2 ) key:value 매핑해주는 Serializer를 등록해서 사용하고 싶은 경우
  • 경우에 따라 Annotation을 붙일 수 없는 경우도 있는데,
    • 다른 라이브러리에 있는 Enum이거나,
    • 붙이게 되면 항상 key-value 형식으로 변환되기 때문에 기존의 Code만으로 변환되는 것과 key-value 형식으로 변환되는 것을 선택해야 한다는 요구사항이 있을 때.
  • 그럴 때는 다음과 같이 BeanSerializer를 통해 변환하도록 유도하는 방식을 사용할 수 있다.
    • 사실 이렇게 library 헤집어서 쓰는걸 별로 좋아하지는 않지만, 요구사항이 있다면 어쩔 수 없지.
  • 처음부터 모든 enum에 JsonFormat을 붙이는 방법이 더 좋지 않을까 싶은게, 결국 이 방법은 컨트롤러에서 리턴할 때 EnumType 이 아니라, 직접 변환이 끝난 Map<String, Object>를 리턴할 수 밖에 없기 때문.

 

```java

public class ObjectMapperUtil {

    /**
     * 기본적으로 jackson은 Enum은 EnumSerializer를 사용해서 변환하며, 이렇게 변환한 결과는 enum {CODE}가 된다.
     * (e.g., CardCode.CCBC 변환 결과는 "CCBC" 가 된다.
     *
     * 그러나 @JsonFormat(shape = JsonFormat.Shape.OBJECT) 이 붙어 있는 enum이 넘어오면, BeanSerializer를 사용해서 변환하며,
     * 이렇게 변환한 결과는 {"field-key":"field-value"} 쌍이 된다.
     * 이 BeanSerializer는 일반적인 객체를 변환할 때 사용하는 serializer이므로, 변환 결과가 key-value가 되는 것이다.
     *
     * 해당 메서드는 @JsonFormat이 붙지 않은 Enum도 key-value 형식으로 변환해주는 ObjectMapper를 구현 및 제공한다.
     * 이를 위해 BeanSerializer를 직접 반환 받아 Custom Serializer로 등록하여 Enum 타입에 대해서도 BeanSerializer가 동작하도록 했다.
     * BeanSerializerFactory.findBeanSerializer(sp, javaType, beanDesc)는
     * BeanSerializer를 얻는 메서드 중 추상화 수준이 가장 높은 메서드이다.
     *
     * @param type
     * @return
     */
    public static ObjectMapper getEnumObjectMapper(Class<?> type) {

        ObjectMapper objectMapper = new ObjectMapper();

 

        BeanSerializerFactory sf = (BeanSerializerFactory)objectMapper.getSerializerFactory();

        SerializationConfig config = objectMapper.getSerializationConfig();

        SerializerProvider sp = objectMapper.getSerializerProviderInstance();

 

        JavaType javaType = config.constructType(type);

        BeanDescription beanDesc = config.introspect(javaType);

        ((BasicBeanDescription) beanDesc).removeProperty("declaringClass");

 

        JsonSerializer<Object> serializer;
        try {
            serializer = sf.findBeanSerializer(sp, javaType, beanDesc);
        } catch (JsonMappingException e) {
            throw new RuntimeException(e);
        }

        SimpleModule module = new SimpleModule();
        module.addSerializer(serializer);
        objectMapper.registerModule(module);

 

        return objectMapper;
    }
}

```

  • class 안에 있는 field의 타입이 Enum인 경우에도 동작하는데, 이런 Enum이 여러 개인 경우에는 serializer를 여러 개 등록해줘야 하므로 메서드를 좀 수정할 필요가 있음!

 

 

방법 2 ) Enum 클래스 내에서 메서드로 제공하고 싶은 경우

  • 그냥 ``java map.put("koName", this.koName);`` 이런 식으로 해도 되지만... 아래는 reflection을 이용해 한방에 처리하는 코드.

```java

public Map<String, String> toMap() {

    return Arrays.stream(this.getDeclaringClass().getDeclaredFields())

        .filter(field -> !field.isSynthetic() && !field.isEnumConstant())

        .collect(Collectors.toMap(

            field -> field.getName(),

            field -> getFieldValue(field)));

}

 

private String getFieldValue(Field field) {

    try {

        return (String)field.get(this);

    } catch (IllegalAccessException e) {

        throw new RuntimeException(e);

    }

}

```