개요
@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으로 손쉽게 변환할 수 있습니다.