Spring
API
직렬화 / 역직렬화

직렬화/역직렬화

  • 직렬화 : 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;
}