Computer >> 컴퓨터 >  >> 프로그래밍 >> Java

Java에서 @JsonSerialize 어노테이션으로 커스텀 직렬화기(Serializer) 구현하는 방법

개요

@JsonSerialize 어노테이션은 Jackson 라이브러리에서 특정 필드를 직렬화하는 과정에 사용자 정의 직렬화기(Custom Serializer)를 적용하고 싶을 때 사용합니다. 기본 직렬화 방식 대신 원하는 JSON 출력 형식을 직접 정의할 수 있어, 데이터 형태를 자유롭게 가공해야 하는 상황에서 매우 유용합니다.

커스텀 직렬화기를 구현하려면 StdSerializer 클래스를 상속받은 뒤, serialize() 메서드를 오버라이드(재정의)하면 됩니다.

문법(Syntax)

@Target(value={ANNOTATION_TYPE,METHOD,FIELD,TYPE,PARAMETER})
@Retention(value=RUNTIME)
public @interface JsonSerialize

@JsonSerialize는 어노테이션 타입, 메서드, 필드, 타입, 파라미터에 모두 적용할 수 있으며, 런타임 시점까지 유지되는 RUNTIME 리텐션 정책을 따릅니다.

예제 코드

아래 프로그램은 @JsonSerialize 어노테이션을 사용하여 커스텀 직렬화기를 구현하는 전체 예제입니다. Employee 객체의 languages 배열을 세미콜론(;)으로 구분된 하나의 문자열로 직렬화하는 방식입니다.

import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.*;
import com.fasterxml.jackson.databind.ser.std.*;

public class JsonSerializeAnnotationTest {
    public static void main(String[] args) throws JsonProcessingException, IOException {
        Employee emp = new Employee(115, "Adithya", new String[] {"Java", "Python", "Scala"});
        ObjectMapper mapper = new ObjectMapper();
        String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(emp);
        System.out.println(jsonString);
    }
}

// 커스텀 직렬화기 클래스
class CustomSerializer extends StdSerializer<Employee> {
    public CustomSerializer(Class<Employee> t) {
        super(t);
    }
    public CustomSerializer() {
        this(Employee.class);
    }
    @Override
    public void serialize(Employee emp, JsonGenerator jgen, SerializerProvider sp)
            throws IOException, JsonGenerationException {
        StringBuilder sb = new StringBuilder();
        jgen.writeStartObject();
        jgen.writeNumberField("id", emp.getId());
        jgen.writeStringField("name", emp.getName());
        for (String s : emp.getLanguages()) {
            sb.append(s).append(";");
        }
        jgen.writeStringField("languages", sb.toString());
        jgen.writeEndObject();
    }
}

// Employee 클래스
@JsonSerialize(using = CustomSerializer.class)
class Employee {
    private int id;
    private String name;
    private String[] languages;

    public Employee(int id, String name, String[] languages) {
        this.id = id;
        this.name = name;
        this.languages = languages;
    }
    public int getId() {
        return this.id;
    }
    public String getName() {
        return this.name;
    }
    public String[] getLanguages() {
        return this.languages;
    }
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("ID: ").append(this.id)
                .append("\nName: ").append(this.name).append("\nLanguages:");
        for (String s : this.languages) {
            sb.append(" ").append(s);
        }
        return sb.toString();
    }
}

동작 방식 설명

핵심 포인트는 다음과 같습니다.

1. CustomSerializer 클래스: StdSerializer<Employee>를 상속하여 serialize() 메서드 안에서 JSON 출력 형식을 직접 제어합니다. 이 예제에서는 languages 배열을 반복 처리하면서 각 요소 뒤에 세미콜론을 붙여 하나의 문자열로 합칩니다.

2. @JsonSerialize(using = CustomSerializer.class): Employee 클래스에 이 어노테이션을 붙이면, Jackson의 ObjectMapper가 해당 객체를 직렬화할 때 기본 직렬화 로직 대신 CustomSerializer를 사용하게 됩니다.

실행 결과(Output)

{
    "id" : 115,
    "name" : "Adithya",
    "languages" : "Java;Python;Scala;"
}

실행 결과를 보면 languages 필드가 배열 형태가 아닌 "Java;Python;Scala;"라는 단일 문자열로 출력된 것을 확인할 수 있습니다. 이처럼 @JsonSerialize 어노테이션과 StdSerializer를 조합하면 복잡한 객체 구조도 원하는 형식의 JSON으로 손쉽게 변환할 수 있습니다.