Jackson 라이브러리 어노테이션 정리

Leo·2024년 4월 13일
0

기본 생성자, getter와 같은 부분을 생략하기 위해서 접근 제어자는 public으로 설정 했습니다.

직렬화 어노테이션

@JsonAnyGetter - Map

public class Member {
    public String name;
    private Map<String, String> properties;

    public Member(String name) {
        this.name = name;
    }

    @JsonAnyGetter
    public Map<String, String> getProperties() {
        return properties;
    }

    public void add(String key, String val) {
        this.properties.put(key, val);
    }
}

Map 필드를 다루는데 유연성을 제공합니다.

@JsonGetter - Getter 메서드 대체

public class Member2 {
    public int id;
    private String name;

    public Member2(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @JsonGetter("name")
    public String otherName() {
        return name;
    }
}

@JsonPropertyOrder - 직렬화 순서

@JsonPropertyOrder({ "name", "id" })
public class Member {
    public int id;
    public String name;

    public Member(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

name, id 순서로 json데이터가 정렬됩니다.

@JsonValue - 직렬화 대상 단일 필드, 단일 메서드

public enum TypeEnumWithValue {
    TYPE1(1, "one"), TYPE2(2, "two");

    private Integer id;
    private String name;

    TypeEnumWithValue(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    @JsonValue
    public String getName() {
        return name;
    }
}

@JsonValue는 전체 인스턴스를 직렬화 할 때 사용하는 단일 메서드입니다.
필드에도 적용이 가능합니다.

역직렬화에도 사용되면 converter 필요없는거 아닌가 라고 생각했는데 직렬화에만 사용됩니다.

@JsonRootName - json root

@JsonRootName(value = "member")
public class Member {
    public int id;
    public String name;

    public Member(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

// @JsonRootName(value = "member") 적용 전후, root로 wrapping

{
  "id": 1,
  "name":"Kim"
}

{
  "Member": {
    "id": 1,
    "name": "Lee"
  }
}

해당 어노테이션이 적용되려면, ObejctMapper의 설정을 변경해야 합니다.
mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);

@JsonSerialize

marshalling 과정에 custom serializer를 사용하도록 지정한다.

Jackson 라이브러리의 StdSerializer를 상속하여 custom serializer를 만들 수 있다.

marshalling과 serialization은 차이가 있다. serialization은 객체를 byte stream으로 변환하는 것 만을 의미한다. 이에 반해 marshalling은 객체를 저장이나 전송을 위해 적당한 자료 형태로 변형하는 것을 의미한다.

StdSerializer or JsonSerializer 를 구현해서 사용

public class CustomSerializer extends JsonSerializer<LocalDate> {
    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    @Override
    public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeString(formatter.format(value));
    }
}

적용

// 특정 필드 적용
public class MyObject {
    @JsonSerialize(using = CustomSerializer.class)
    private LocalDate date;
    // ...
}

// 공통 적용
@JsonComponent
public class CustomSerializer extends JsonSerializer<LocalDate> {**

bean으로 사용 가능하지만 @JsonComponent로 등록하면 직렬화될 때, 자동으로 해당 포멧에 사용됩니다.

@JsonProperty - 직렬화, 역직렬화에 해당 이름 사용

@JsonProperty("MyNameIs")
    private String name;

역직렬화 어노테이션

@JsonCreator - 역직렬화 생성자 or 팩토리 지정

json 데이터의 key값이 원하는 형태로 오지 않는 경우, 변경해서 역직렬화를 할 수 있다.

public class Member {

    public int id;
    public String name;

    @JsonCreator
    public Member(@JsonProperty("id") int id, @JsonProperty("otherName") String name) {
        this.id = id;
        this.name = name;
    }
}

@JsonAnySetter - map 객체에 역직렬화 활용


public class Member {

    private String name;
    private Map<String, String> properties = new HashMap<>();

    @JsonAnySetter
    public void add(String key, String value) {
        properties.put(key, value);
    }

    public String getName() {
        return name;
    }

    public Map<String, String> getProperties() {
        return properties;
    }

    public static void main(String[] args) throws JsonProcessingException, ParseException {

        String json = "{\"name\":\"Jay\",\"favorite\":\"chicken\",\"hobby\":\"tennis\"}";

        Member member = new ObjectMapper().readerFor(Member.class).readValue(json);

        System.out.println(member.getProperties());

    }
}

@JsonDeserialize - 역직렬화 형식 지정

public class Event {
    public String name;

    @JsonDeserialize(using = CustomDateDeserializer.class)
    public Date eventDate;
}

---------------------------------------------------------------
public class CustomDateDeserializer extends StdDeserializer<Date> {
    private static SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    public CustomDateDeserializer() {
        this(null);
    }

    public CustomDateDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public Date deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
        String date = parser.getText();
        try {
            return formatter.parse(date);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

@JsonAlias - 역직렬화 대상 이름을 여러개 지정

public class Member {
    @JsonAlias({"name", "his_name", "her_name"})
    public String name;
    public String hobby;
}

직렬화 무시 어노테이션

@JsonIgnoreProperties, @JsonIgnore - 필드제외

@JsonIgnoreProperties({"id"}) // 클래스 레벨
public class Member {

------------------------------------------------------------
@JsonIgnore // 필드 레벨
public int id;

@JsonInclude - null 제외

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Member {

@JsonAutoDetect - 직렬화 접근제어자로 대상 선정

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
// private 필드까지 직렬화 대상
// sonAutoDetect의 가시성 범위는 ANY, NON_PRIVATE, PROTECTED_AND_PUBLIC 등등

모든 jackson 어노테이션 비활성화

아래와 같은 옵션을 준 objectMapper를 사용하자.

public void disable_jackson_annotation() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.disable(MapperFeature.USE_ANNOTATIONS);
    ...
}

사례

특정 deserializer를 사용하는 DTO가 있는데, 해당 DTO로 특정 값을 hash하고 있는 상황에서,

해당 DTO로 데이터를 받으면서 hashing 하고, 다시 외부와 통신하고, 다시 데이터를 받을 때, 해당 DTO로 받을 때, 두 번 hasing되는 문제가 발생했다고 합니다.

따라서 해당 DTO를 다시 사용하려면 어노테이션을 비활성화한 objectMapper를 통해서 해결 할 수 있습니다.

참고

https://www.baeldung.com/jackson-annotations

0개의 댓글