HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🤩
개발
/
Spring
Spring
/
🧑‍🤝‍🧑
Object Mapper
🧑‍🤝‍🧑

Object Mapper

Object Mapper 를 이용한 Serialize, DeserializeObjectMapper.writeValueAsString 내부 BeanSerializer 호출로직ObjectMapper.readValue 내부 BeanDeserializer호출 로직Object Mapper로 Object ↔ MapMap → Object, Timestamp 값 LocalDateTime으로 변환시 발생하는 에러Object Mapper를 활용해 JSON 다루기Json 만들어내기HttpServletRequest로 request body 읽기JavaTimeModuleJavaTimeModule 추가LocalDateTime, LocalDate 패턴 → @JsonFormat 을 이용@JsonInclude

Object Mapper 를 이용한 Serialize, Deserialize

  • com.fasterxml.jackson.databind.ObjectMapper
  • Serialize : object를 메모리, 파일, DB등에 저장하기 위해서 바이트의 스트림으로 변환하는 과정
@Test void test() { var objectMapper = new ObjectMapper(); // serialize var user = new UserRequest( "geuno", "rlfrmsdh1@gmail.com", 20); var text = objectMapper.writeValueAsString(user); System.out.println(text); // deserialize var objectUser = objectMapper.readValue(text, UserRequest.class); System.out.println(objectUser); }
  • object → json (serialize): getter가 필요함 (writeValueAsString)
  • json → object (deserialize) : setter는 필요 없고 default Constructor만 존재하면 됨
    • nested class에서 AllArgsConstructor로만 동작을 하는 것을 확인함. default 생성자 있으면 아예 매핑이 안되었음
      • RequestMappingHandlerAdapter. invokeHandlerMethod() [line 552]
      • ServletInvocableHandlerMethod invokeAndHandle() [line 50]
      • InvocableHandlerMethod invokeForRequest() [line 56] → getMethodArgumentValues()
        • args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
  • 예외점 : record 클래스는 따로 뭐 하지않아도 동작이 가능함
    • 즉,field 이름으로 자동 생성되는 getter를 이용하고, default 생성자는 없지만 그 부분은 내부적으로 처리가 되나봄

ObjectMapper.writeValueAsString 내부 BeanSerializer 호출로직

// BeanSerializer.java @Override public final void serialize(Object bean, JsonGenerator gen, SerializerProvider provider) throws IOException { if (_objectIdWriter != null) { gen.setCurrentValue(bean); // [databind#631] _serializeWithObjectId(bean, gen, provider, true); return; } gen.writeStartObject(bean); if (_propertyFilterId != null) { serializeFieldsFiltered(bean, gen, provider); } else { serializeFields(bean, gen, provider); } gen.writeEndObject(); } //serializeFields protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider) throws IOException { final BeanPropertyWriter[] props; if (_filteredProps != null && provider.getActiveView() != null) { props = _filteredProps; } else { props = _props; } int i = 0; try { for (final int len = props.length; i < len; ++i) { BeanPropertyWriter prop = props[i]; if (prop != null) { // can have nulls in filtered list prop.serializeAsField(bean, gen, provider); } } if (_anyGetterWriter != null) { _anyGetterWriter.getAndSerialize(bean, gen, provider); } ,,, }
  • 여기서 serializeFields 안에서 get메서드가 존재하는지{getName() } record의 field 접근방식{ name() } 으로 접근 가능한지를 resolve해주는 듯함
record를 사용했을 시, 생성되는 기본 메서드로 writeValueAsString진행함
record를 사용했을 시, 생성되는 기본 메서드로 writeValueAsString진행함
class에서 getter정의 시, 생성되는 getter로 진행함
class에서 getter정의 시, 생성되는 getter로 진행함

ObjectMapper.readValue 내부 BeanDeserializer호출 로직

  • BeanDeserializer class안에서 vanillaDeserialize() 함수 호출
private final Object vanillaDeserialize(JsonParser p, DeserializationContext ctxt, JsonToken t) throws IOException { final Object bean = _valueInstantiator.createUsingDefault(ctxt); // [databind#631]: Assign current value, to be accessible by custom serializers p.setCurrentValue(bean); if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { String propName = p.currentName(); do { ...' }
 

Object Mapper로 Object ↔ Map

Map → Object, Timestamp 값 LocalDateTime으로 변환시 발생하는 에러

15) "createdAt" 16) "2023-04-04 15:44:38"
Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module \"com.fasterxml.jackson.datatype:jackson-datatype-jsr310\" to enable handling\n at [Source: UNKNOWN; byte offset: #UNKNOWN]
의존성 추가 & ObjectMapper에 registerModule 함수 호출
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.3' ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());
그 후 time format 명시(@JsonFormat)해주어야 함
Cannot deserialize value of type `java.time.LocalDateTime` from String \"2023-04-04 15:44:38\": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2023-04-04 15:44:38' culd not be parsed at index 10
JsonFormat 명시하지 않을 시 발생하는 에러
public class Room { @Id private Integer id; private Integer channelId; private int roomType; private String roomName; private Integer hostId; private int userCnt; private int maxUserCnt; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createdAt; }

Object Mapper를 활용해 JSON 다루기

// json 파일로부터 객체 읽기 ObjectMapper objectMapper = new ObjectMapper(); User user = objectMapper.readValue(new File("./src/main/java/sample.json"), User.class); System.out.println(user); // json array로 부터 리스트 뽑기 List<User> users = objectMapper.readValue<List<User>>(new File("./src/main/java/sample.json")); // json의 요소별로 값을 얻고 싶을때 String json = objectMapper.writeValueAsString(user); JsonNode jsonNode = objectMapper.readTree(json); String _name = jsonNode.get("name").asText(); int _age = jsonNode.get("age").asInt(); JsonNode cars = jsonNode.get("cars"); ArrayNode arrayNode = (ArrayNode) cars; List<Car> _cars = objectMapper.convertValue(arrayNode, new TypeReference<List<Car>>() {}); //json의 요소 값을 변경하고싶을때 ObjectNode objectNode = (ObjectNode) jsonNode; objectNode.put("name", "steve"); objectNode.put("age", 20); System.out.println(objectNode.toPrettyString());
  • 주요 클래스
    • JsonNode
    • ArrayNode
    • ObjectNode
    • JsonNode, ArrayNode, ObjectNode 차이

Json 만들어내기

ObjectNode node = objectMapper.createObjectNode(); ObjectNode articleNode = node.putObject("article"); articleNode.put("title", article.getTitle()); articleNode.put("description", article.getDescription()); articleNode.put("body", article.getBody()); ArrayNode tagList = articleNode.putArray("tagList"); for(Tag tag : article.getTags()) tagList.add(tag.getName()); /* { "article": { "title": "How to train your dragon", "description": "Ever wonder how?", "body": "You have to believe", "tagList": ["reactjs", "angularjs", "dragons"] } } */

HttpServletRequest로 request body 읽기

// HttpServletRequest -> object UserLoginForm userLogin = objectMapper.readValue(request.getInputStream(), UserLoginForm.class); // HttpServletResponse response.getOutputStream().write(objectMapper.writeValueAsBytes(user));

JavaTimeModule

  • Java 8 date/time API(LocalDate, LocalDateTime, ZonedDateTime) 의 serialize, deserialize를 도와줌
  • 이 때, @JsonFormat은 Jackson 의해 제공되는 어노테이션으로 serialize, deserialize를 할 때 해당 date/time 의 custom format을 명시할 수 있음

JavaTimeModule 추가

ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());

LocalDateTime, LocalDate 패턴 → @JsonFormat 을 이용

public class DateType { @JsonFormat(pattern = "yyyy-MM-dd") private LocalDate date; @JsonFormat(pattern = "kk:mm:ss") private LocalTime time; @JsonFormat(pattern = "yyyy-MM-dd kk:mm:ss") private LocalDateTime dateTime; }
  • pattern string 형태 참조 : SimpleDateFormat javadoc

@JsonInclude

  • VO객체를 JSON으로 변환할 때 포함되면 안되는 변수에 @JsonIgnore
  • VO 객체를 Json으로 변환할 때 포함할 변수들 @JsonInclude