직렬화/역직렬화
- 직렬화 : Object -> Json
- 역직렬화 : Json -> Object
Gson
Json 객체를 자바 모델로 변환해주는 라이브러리
기본 사용법
public static void main(String[] args) {
Member member = new Member("홍길동", 30, "rookedsysc36@gmail.com");
// Model to Json
Gson gson = new Gson();
String json = gson.toJson(member);
// Json to Model
json = "{\"name\":\"홍길동\",\"age\":30,\"email\":\"rookedsysc36@gmail.com\"}";
Member fromJson = gson.fromJson(json, Member.class);
System.out.println(json);
System.out.println(fromJson);
}
Model을 변수로 갖는 경우
public class Main {
public static void main(String[] args) {
Gson gson = new Gson();
String jsonString = "{\"name\":\"John Doe\",\"age\":\"30\",\"email\":\"johndoe@example.com\",\"addr\":{\"country\":\"United States\",\"city\":\"San Francisco\"}}";
Person person = gson.fromJson(jsonString, Person.class);
System.out.println(person.toString());
}
}
ObjectMapper
@Autowired
private ObjectMapper objectMapper;
@Test
void contextLoads() throws JsonProcessingException {
var user = new UserRequest();
user.setUserName("홍길동");
user.setAge(20);
user.setUserEmail("rookedsysc36@gamil.com");
// object -> json 직렬화
var json = objectMapper.writeValueAsString(user);
System.out.println(json);
// json -> object 역직렬화
var dto = "{\"user_name\":\"홍길동\",\"user_email\":\"rookedsysc36@gamil.com\",\"age\":20}";
UserRequest obejctUser = objectMapper.readValue(dto, UserRequest.class);
System.out.println(obejctUser);
}
Config으로 등록
아래와 같이 ObjectMapper를 Bean으로 등록해주면 Spring이 실행될 때 ObjectMapper를 불러오는데,
@Entity Annotation이 붙어있는 클래스에 대해서 해당 ObjectMapper가 동작
하게 된다.
@Configuration
public class ObjectMapperConfig {
// Spring이 실행될 때 Object Mapper를 불러오는데 설정된 Object Mapper가 없으면
// Spring이 임의적으로 Object Mapper를 생성하고 설정된 Object Mapper가 있다면 설정된 Mapper를 불러오게 된다.
@Bean
public ObjectMapper objectMapper() {
var objectMapper = new ObjectMapper();
// JDK 버전 이후 클래스 사용 가능
objectMapper.registerModule(new Jdk8Module());
objectMapper.registerModule(new JavaTimeModule());
// 모르는 Json Value에 대해서는 무시하고 나머지 값만 파싱한다.
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 비어있는 Bean을 만들 때
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
// 날짜 관련 직렬화 설정 해제
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 스네이크 케이스
objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategies.SnakeCaseStrategy());
return objectMapper;
}
}
ObjectMapper의 동작 분석하기
UserRequest class를 아래와 같이 자동으로 생성되는 setter, getter 등을 제외해주고
public class UserRequest {
private String userName;
private String userEmail;
private int age;
public UserRequest(String userName, String userEmail, int age) {
this.userName = userName;
this.userEmail = userEmail;
this.age = age;
}
public String getName() {
return this.userName;
}
public String getPersonEmail() {
return this.userEmail;
}
public int getBornIn() {
return this.age;
}
}
@SpringBootTest
class RestApiApplicationTests {
// Spring에서 관리하는 Bean을 주입받는다.
@Autowired
private ObjectMapper objectMapper;
@Test
void contextLoads() throws JsonProcessingException {
UserRequest user = new UserRequest("홍길동", "rookedsysc36@gmail.com", 30);
var json = objectMapper.writeValueAsString(user);
System.out.println(json);
}
}
contextLoads() 메서드를 실행시켜주면 결과가
"personEmail":"rookedsysc36@gmail.com","bornIn":30,"name":"홍길동"
처럼 출력되는걸 알 수 있다 이를 통해 알 수 있는 것은 ObjectMapper가 Method를 기반으로 동작한다는 사실이다.
메서드에서 get 이후에 나오는 문자열
을 기준으로 Json의 Key를 생성한다는 것을 알 수 있다.
에러 발생 유형
위의 예제를 통해 아래와 같은 에러가 발생할 수 있음을 알 수 있다. 만약에 공손해지기 위해서 getUserName이 있는데 또 다른 getUserName을 선언했다고 가정하자.
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public class UserRequest {
private String userName;
private String userEmail;
private int age;
public String getUserName() {
return this.userName + " 입니다.";
}
}
그리고 이걸 Json으로 Mapping을 하게 되면 아래와 같이 출력이 된다.
{"user_name":"홍길동 입니다.","user_email":"rookedsysc36@gmail.com","age":30}
이는 우리가 원하는 방향대로 Json Mapping이 된게 아니므로 다음과 같이 JsonIgnore
annotation을 사용해서 이를 무시할 수 있다.
@JsonIgnore
public String getUserName() {
return this.userName + " 입니다.";
}
JsonProperty 사용
getter와 setter를 사용하지 않는 방법으로는 각각의 property에 JsonProperty
annotation을 사용하는 방법이 있다.
public class UserRequest {
@JsonProperty("user_name")
private String userName;
@JsonProperty("user_email")
private String userEmail;
@JsonProperty("user_age")
private int age;
}