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)을 정해 놓고 코딩을 하면 프로젝트 관리할 때 편하다.
'JAVA' 카테고리의 다른 글
그래프 DFS(위상정렬) (0) | 2021.07.20 |
---|---|
JAVA final 키워드 (0) | 2020.06.26 |