티스토리 뷰
map.toString
을 검색해서 들어오시는 분들이 많은 거 같아 간단히 설명을 추가합니다.
Map
인터페이스의 구현체(대표적으로 HashMap
등)에서 toString
을 호출하면 내부적으로 Map
을 탐색하는 Iterator
를 만들어 각 Entry
를 key=value
형식의 문자열로 변환해줍니다.
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class Scratch {
public static void main(String[] args) {
Map<String, Object> map = new HashMap<>();
map.put("boolean", true);
map.put("list", List.of("a", "b", "c"));
map.put("integer", 1);
map.put("String", "abc");
System.out.println(map);
}
}
이 소스 코드를 실행하면
{boolean=true, integer=1, String=abc, list=[a, b, c]}
이런 결과가 출력됩니다.
Map
은 중괄호({}
)로 표현하고, List
는 대괄호([]
)로 표현합니다.
이는 각각 Map
과 List
구현체의 toString
을 따르기 때문입니다.
Map
안에 Map
이나 List
를 중첩해도 마찬가지로 표현됩니다.
Map
안에 커스텀 클래스를 포함하더라도 커스텀 클래스의 toString
이 호출되어 출력됩니다.
예를 들어,
import java.util.*;
class Scratch {
public static void main(String[] args) {
Map<String, User> users = new HashMap<>();
User user = new User("홍길동", 10, false);
users.put(user.name, user);
System.out.println(users);
}
private static class User {
String name;
int age;
boolean isMarried;
public User(String name, int age, boolean isMarried) {
this.name = name;
this.age = age;
this.isMarried = isMarried;
}
@Override
public String toString() {
return new StringJoiner(", ", User.class.getSimpleName() + "[", "]")
.add("name='" + name + "'")
.add("age=" + age)
.add("isMarried=" + isMarried)
.toString();
}
}
}
이렇게 커스텀 클래스 User
를 작성(내부적으로 사용할 거라 private static
클래스로 정의하였고 getter/setter
등을 만들지 않았습니다)하고 기본적인 toString
(보통 IDE
에서 지원해주는)을 override
한 소스 코드가 있을 때, 이를 실행해보면,
{홍길동=User[name='홍길동', age=10, isMarried=false]}
이렇게 출력되는 것을 확인할 수 있습니다.
toString
을 따라 출력되는 형태가 변한다고 했으므로 User
의 toString
을 아래처럼 수정하면,
@Override
public String toString() {
return String.format("%s은(는) %d살이고 결혼을 %s습니다.", name, age, isMarried ? "했" : "안 했");
}
{홍길동=홍길동은(는) 10살이고 결혼을 안 했습니다.}
이렇게 출력됩니다.
두 줄 요약:
- {key1=value2, key2=value2, ...} 형식으로
Map
안의Entry
가 출력됨(LinkedHashMap
을 사용하면Entry
의 순서가 보장되어 출력) key
와value
는 각각의 타입(클래스)에서 구현한toString
을 이용해 문자열로 변환하여 출력됨
여기부터는 기존 본문으로 map.toString()을 통해 출력한 문자열을 다시 Map으로 만드는 과정입니다.
며칠 전 서버 로그를 분석하다가 JSON
포맷이 아닌 일반적인 Map
구현체의 toString
형태를 발견하게 되었습니다.
디버깅을 위해서나 로컬에서는 자주 확인할 수 있는 형식이죠.
Map<String, Object> map = new HashMap<>();
map.put("boolean", true);
map.put("list", Arrays.asList("a", "b", "c"));
map.put("integer", 1);
map.put("String", "abc");
System.out.println(map);
간단히 위와 같은 소스 코드를 실행시켜보면
{boolean=true, integer=1, String=abc, list=[a, b, c]}
이런 결과가 출력됩니다.
이렇게 단순한 경우엔 마치 HTTP
요청에서 query parameter
를 파싱하듯이 ",", "=" 등을 이용해 키와 값 등을 구분할 수 있으나, 로그에서 저 부분만 긁어와서 JSON
형태로 변환하는 작업은 생각보다 쉽지가 않습니다.
특히 커스텀 클래스가 상속관계를 가지고 있어 출력형태가 위의 예시처럼 단순하지 않은 경우 도저히 파싱할 엄두가 안 나더군요.
사실 map
을 출력하는 부분의 소스 코드를 수정하여 확인해도 되지만 이미 스테이징 환경에 배포되어있는 모듈의 로그라서 괜히 건드리고싶지 않았고, 단순 디버깅을 위해 직렬화/역직렬화를 하기엔 성능 이슈 또한 고려해야 했기에(로컬이었으면 바로 ObjectMapper
써서 출력했을텐데..) 다른 방법을 찾아야 했습니다.
찾아보니 java.util.Properties
를 활용한 방법이 있었습니다.
Properties
는 일반적으로 key=value
형식의 파일을 읽어 접근할 수 있는 인터페이스를 제공합니다.
이를 이용하면 위의 로그가 다시 Properties
로 역직렬화 되고, Properties
의 인터페이스를 활용한다면 얼마든지 편하게 로그의 데이터에 접근할 수 있습니다.
public static void main(String[] args) throws IOException {
byte[] allBytes = Files.readAllBytes(Paths.get("filename");
String str = new String(allBytes, StandardCharsets.UTF_8);
Properties props = new Properties();
props.load(new StringReader(str.substring(1, str.length() - 1).replace(", ", "\n")));
Map<String, String> map = new HashMap<>();
for (Map.Entry<Object, Object> e : props.entrySet()) {
map.put((String) e.getKey(), (String) e.getValue());
}
System.out.println(map);
}
Properties
로 불러온 뒤 Properties
의 entrySet
을 순차적으로 탐색하면서 Map
에 다시 넣어주면 Map
인터페이스를 사용할 수 있습니다.
하지만 이 방법도 List
내부에 ','가 존재한다든지 복잡한 문자열에 대해선 제대로 파싱하지 못하더군요 ㅜㅜ
그냥 ObjectMapper
씁시다. (😹두 번 씁시다)
'Java' 카테고리의 다른 글
Java, JVM, JDK, JRE는 어떻게 다른가? (0) | 2020.07.21 |
---|---|
URL을 이용하여 파일 다운 받기 (0) | 2020.06.17 |
Jackson의 모든 것 - Optional (0) | 2019.12.04 |
Jackson의 모든 것 - 커스터마이징 편 (0) | 2019.11.26 |
Jackson의 모든 것 - 일반편 (0) | 2019.11.22 |
- Total
- Today
- Yesterday
- 함께 자라기 후기
- 스프링 데이터 jpa
- JSON
- Jackson
- proto3
- gRPC
- 클린 아키텍처
- spring boot jwt
- 함께 자라기
- 스프링 부트 애플리케이션
- 스프링 부트 튜토리얼
- 스프링 부트 회원 가입
- 스프링 부트
- Linux
- Spring Boot
- Spring Boot Tutorial
- spring boot application
- QueryDSL
- 스프링부트
- leetcode
- Java
- spring boot app
- Spring Boot JPA
- JPA
- 헥사고날 아키텍처
- 알고리즘
- Spring Data JPA
- intellij
- @ManyToOne
- r
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |