1. 시나리오

여러개의 클래스중 아래의 Person 클래스만 그리고 Person 중에서
'@시리얼할래'가 적용된 필드에 한해서 serialize 하고싶다.

@Person클래스만
public class Person {
    @시리얼할래
    private String firstName;
    @시리얼할래
    private String lastName;
    private String age;
    private String address;
}

2. custom annotation 작성

JsonSerializable(Person클래스만) annotation 만든다
아래의 옵션을 간략히 설명하자면 언제? 런타임시, 타겟은? 클래스나 인터페이스

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(TYPE)
public @interface JsonSerializable {

}

 

@JsonElement(시리얼할래) annotation 만든다
아래의 옵션을 간략히 설명하자면 언제? 런타임시, 타겟은? 필드에 적용

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target({ FIELD })
public @interface JsonElement {
    public String key() default "";
}

 

Person 클래스에 적용

@JsonSerializable
public class Person {
    @JsonElement
    private String firstName;
    @JsonElement
    private String lastName;
    @JsonElement
    private String age;

    private String address;

3. object to json 시리얼 클래스 작성

checkIfSerializable 메소드를 보면 object.getClass()를 이용해 해당 오브젝트를 가져오고
clazz.isAnnotationPresent(JsonSerializable.class)을 통해 @Person클래스만 annotation이 있는지 확인한다.
말그대로 Person 클래스인지 확인 후 아니면 런타임 Exception을 던진다.

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

public class ObjectToJsonConverter {
    private void checkIfSerializable(Object object) {
        if (Objects.isNull(object)) {
            throw new RuntimeException("Can't serialize a null object");
        }

        Class<?> clazz = object.getClass();
        if (!clazz.isAnnotationPresent(JsonSerializable.class)) {
            throw new RuntimeException("The class " + clazz.getSimpleName() + " is not annotated with JsonSerializable");
        }
    }
}

 

getJsonString 메소드는 object의 모든 필드를 찾은 뒤 @시리얼할래 annotation 있는지 확인하는 코드다.

(있으면 시리얼 실시)

private String getJsonString(Object object) throws IllegalArgumentException, IllegalAccessException {
    Class<?> clazz = object.getClass();
    Map<String, String> jsonElementsMap = new HashMap<>();
    for (Field field : clazz.getDeclaredFields()) {
        field.setAccessible(true);
        if (field.isAnnotationPresent(JsonElement.class)) {
            jsonElementsMap.put(field.getName(), (String) field.get(object));
        }
    }

    String jsonString = jsonElementsMap.entrySet()
        .stream()
        .map(entry -> "\"" + entry.getKey() + "\":\"" + entry.getValue() + "\"")
        .collect(Collectors.joining(","));
    return "{" + jsonString + "}";
}

5. 테스트 코드 작성

첫번째는 일반 object가 시리얼이 되는지 체크하는 테스트다.(Person만 통과)
두번째는 Person object가 정상적으로 시리얼 됐는지 확인하는 테스트.

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.api.Test;

public class JsonSerializerUnitTest {

    @Test
    public void givenObjectNotSerializedThenExceptionThrown() throws JsonSerializationException {
        Object object = new Object();
        ObjectToJsonConverter serializer = new ObjectToJsonConverter();
        assertThrows(RuntimeException.class, () -> {
            serializer.convertToJson(object);
        });
    }

    @Test
    public void givenObjectSerializedThenTrueReturned() throws JsonSerializationException {
        Person person = new Person("soufiane", "cheouati", "34");
        ObjectToJsonConverter serializer = new ObjectToJsonConverter();
        String jsonString = serializer.convertToJson(person);
        assertEquals("{\"firstName\":\"soufiane\",\"lastName\":\"cheouati\",\"age\":\"34\"}", jsonString);
    }
}

결론

여러 클래스가 있을때 시리얼 하고 싶은 클래스 필드에 "@시리얼할래" annotation만 박으면 알아서 시리얼을 해주는 공통된 약속(annotation)을 정해 놓고 코딩을 하면 프로젝트 관리할 때 편하다.

 

 

※ 출처 : www.geeksforgeeks.org/annotations-in-java/

'JAVA' 카테고리의 다른 글

그래프 DFS(위상정렬)  (0) 2021.07.20
JAVA final 키워드  (0) 2020.06.26

+ Recent posts