티스토리 뷰

반응형

Timestamp 자료형 데이터를 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();

     }
}

 

 

 

 

반응형
댓글
공지사항