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해주는 듯함


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
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