티스토리 뷰
[JAVA] Gson으로 LocalDateTime 데이터를 format하여 Object, Array로 변형하기
쩨리쩨리 2023. 2. 8. 14:49Timestamp 자료형 데이터를 LocalDateTime으로 받아 출력하면 날짜가 원하는 format으로 나타나지 않고 아래와 같이 응답받는 문제를 발견했다.
{"regdate": {
"date": {
"year": 2023,
"month": 1,
"day": 9
},
"time": {
"hour": 14,
"minute": 41,
"second": 39,
"nano": 0
}
}
}
날짜 데이터를 timestamp 형식으로 변형하기 위해 직렬화/역직렬화가 쉬운 Gson을 사용하기로 했다.
우선 Gson 모듈을 추가해준다.
implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1'
implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.14.2'
JSR(Java Specification Request)는 자바 플랫폼의 자바 사양을 정의한 문서로, 이중에서 310은 날짜와 관련된 API를 정의한다. JPA를 사용할 때 java 버전에 따라 LocalDateTime 핸들링 이슈가 발생할 수 있기에 추가해주었다. DB를 사용하지 않으면 추가해줄 필요가 없다.
아래와 같이 데이터를 담을 POJO 클래스가 있다.
@Getter
@Setter
@NoArgsConstructor
public class Member {
private String name;
private String phone;
private LocalDateTime regdate;
}
Member 객체에 json 데이터를 매핑한 뒤 LocalDateTime 데이터를 역직렬화(Json -> java 객체) 해보겠다.
LocalDateTime으로 변환하기 위해서 아래와 같은 설정 클래스를 하나 만든다. 직렬화, 역직렬화시 원하는 날짜 포맷을 직접 커스텀 할 수 있다. 본인은 날짜가 "yyyy-MM-dd HH:mm:ss"의 timestamp 형식으로 변형되길 원한다.
import com.google.gson.*;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Configuration
public class LocalDateTimeSerializer implements JsonSerializer<LocalDateTime>, JsonDeserializer<LocalDateTime> {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public JsonElement serialize(LocalDateTime localDateTime, Type srcType, JsonSerializationContext context) {
return new JsonPrimitive(formatter.format(localDateTime));
}
@Override
public LocalDateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return LocalDateTime.parse(json.getAsString(), formatter);
}
}
Gson을 사용하기 위해선 GsonBuilder 사용이 필수이다. GsonBuilder는 Gson 인스턴스를 여러가지 셋팅을 넣어 사용할 수 있도록 도움을 주는 클래스다. Gson이 우리가 만든 커스텀 클래스를 참조하도록 아래와 같이 작성한다.
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer());
Gson gson = gsonBuilder.setPrettyPrinting().create();
String strJson = gson.toJson(object);
Object 객체로 만들기
위에서 만든 Member 객체로 변형하기 위해서 ObjectMapper를 사용할 것이다. ObjectMapper는 Json과 자바 객체간의 변환 기능을 제공한다.
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
// 1. object 변형
Member member = mapper.readValue(strJson, Member.class);
Array 객체로 만들기
Gson으로 역직렬화 할 때 알아야할 점이있다. Gson은 직렬화, 역직렬화 기능을 제공하지만 예외 사항이 있다. Gson은 모든 형태의 직렬화를 제공하지만, 역직렬화를 할 때는 Collection은 역직렬화가 불가능하다. Gson이 전달받은 Json 값을 어떤 타입으로 매핑해야하는지 구체적으로 알 수 없기 때문이라 한다.
즉, ObjectMapper로 원하는 형태의 객체 변형은 가능하지만 LocalDateTime 데이터는 원하는대로 포맷되지 않는다.
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
// 2. List 변형(1)
List<Member> memberList
= mapper.readValue(strJson, new TypeReference<List<Member>>() {});
// 3. List 변형(2)
Type listType = new TypeToken<List<Member>>(){}.getType();
List<Member> memberList = gson.fromJson(strJson, listType);
// 4. List 변형(3)
List<Member> memberList
= Arrays.asList(mapper.readValue(strJson, Member[].class));
// 5. HaspMap 변형
HashMap<Object, Object> memberMap = mapper.readValue(strJson, HashMap.class);
위의 List나 HashMap은 Date format 변형이 안 된 데이터가 들어있다. 그럼 배열 객체를 파싱할 수 있는 방법은 없는 걸까?
Gson Parser를 사용하면 배열로 변형이 가능하다. 아래처럼 JsonParser를 사용하면 LocalDateTime형으로 배열 데이터도 변형이 가능하다.
JsonArray valueArray = JsonParser.parseString(strJson).getAsJsonArray();
코드 풀버전
import com.google.gson.*;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
public class LocalDateTimeTest {
private final MemberRepository memberRepository;
public class Member {
private String name;
private String phone;
private LocalDateTime regdate;
}
@Configuration
public class LocalDateTimeSerializer implements JsonSerializer<LocalDateTime>, JsonDeserializer<LocalDateTime> {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public JsonElement serialize(LocalDateTime localDateTime, Type srcType, JsonSerializationContext context) {
return new JsonPrimitive(formatter.format(localDateTime));
}
@Override
public LocalDateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return LocalDateTime.parse(json.getAsString(), formatter);
}
}
public static void main(String[] args) {
// DB 조회
Member member = memberRepository.fineById(1);
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer());
Gson gson = gsonBuilder.setPrettyPrinting().create();
String strJson = gson.toJson(member);
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
// 1. object 변형
Member memberObject = mapper.readValue(strJson, Member.class);
// 2. List 변형(1)
List<Member> memberList1
= mapper.readValue(strJson, new TypeReference<List<Member>>() {});
// 3. List 변형(2)
Type listType = new TypeToken<List<Member>>(){}.getType();
List<Member> memberList2 = gson.fromJson(strJson, listType);
// 4. List 변형(3)
List<Member> memberList3
= Arrays.asList(mapper.readValue(strJson, Member[].class));
// 5. HaspMap 변형
HashMap<Object, Object> memberMap = mapper.readValue(strJson, HashMap.class);
// 6. JsonArray 변형
JsonArray memberArray = JsonParser.parseString(strJson).getAsJsonArray();
}
}