Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
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 31
Tags
more
Archives
Today
Total
관리 메뉴

개발자 꼬부기의 성장일기

22일차 - StringifyJSON 구현하기 본문

언어공부/[코드스테이츠] 백엔드부트캠프

22일차 - StringifyJSON 구현하기

다죵 2023. 5. 11. 23:31

Stringify는 JSON 형태의 데이터로 변환하는 기능

 


JSON 데이터

JSON 데이터는 이름과 값의 쌍으로 구성.

이러한 JSON 데이터는 데이터 이름, 콜론(:), 값의 순서로 구성.

문법

"데이터이름"

 

데이터의 이름이 "name"이고, 값은 "식빵"이라는 문자열을 갖는 JSON 데이터

예제

"name""식빵"

 

데이터의 이름도 문자열이므로, 항상 큰따옴표("")와 함께 입력해야 한다.

데이터의 값으로 문자열, 숫자, 불리언, 배열, 객체로 올 수  있다.

 

문자열 => "문자열"

숫자 => 숫자

불리언 => 불리언

객체 => []

배열 => []

Map => {key:value}

 


1. stringify함수 구현하기

1-1. instanceof 연산자를 통해 모든 경우의 수로 분기시킨다.

//입력된 값이 문자열일 경우
if (data instanceof String)

//입력된 값이 Integer일 경우
if (data instanceof Integer) 

//입력된 값이 Boolean일 경우
if (data instanceof Boolean)

//입력된 값이 Object[]일 경우 : 배열
if (data instanceof Object[])

//입력된 값이 HashMap일 경우
if (data instanceof HashMap)

1-2. 가장 마지막의 리턴값 부터 채우고 차례대로 리턴값을 구한다. 

	//입력된 값이 문자열일 경우
    if (data instanceof String) 
    	return String.format("\"%s\"", data );;

    //입력된 값이 Integer일 경우
    if (data instanceof Integer) 
    	return Integer.toString((Integer) data);

    //입력된 값이 Boolean일 경우
    if (data instanceof Boolean) 
    	return String.valueOf((Boolean) data);

    //입력된 값이 Object[]일 경우 : 배열
    if (data instanceof Object[]) {
      //여기서 바로 하려다가 '[ ]' 필요해서 함수 분기 시킴
      String returnStr = arrayToString((Object[]) data);
      return "[" + returnStr + "]";
    }

    //입력된 값이 HashMap일 경우
    if (data instanceof HashMap) {
      //여기서 바로 하려다가 '{ }' 필요해서 함수 분기 시킴
      //맵 사이즈가 없으면 빈값이니 {}로 출력됨
      if (((HashMap<?, ?>) data).size() == 0){
        return "{}";
      }

      String returnStr = mapToString((HashMap<?,?>) data);
      return "{" + returnStr + "}";

    }

    //지정되지 않은 타입의 경우에는 "null"을 리턴합니다.
    return "null";

* 특수문자 " 큰따옴표 등은 \" 앞에 \(역슬래시)가 붙어야 한다.

 

* 정수를 문자열로 변환하는 방법 4가지

  1.  Integer.toString():
    • 정수를 문자열로 변환하기 위해 toString() 메서드를 사용
    • 간단하고 직관적인 방법
    • null을 처리할 수 없으며, 정수 값이 null인 경우 NullPointerException이 발생
    • 예시: String str = Integer.toString(number);
  2. String.valueOf():
    • String 클래스의 valueOf() 메서드를 사용하여 정수를 문자열로 변환
    • null을 처리할 수 있으며, 정수 값이 null인 경우 "null" 문자열로 변환
    • 예시: String str = String.valueOf(number);
  3. String.format():
    • 문자열 포맷을 사용하여 정수를 문자열로 변환
    • 다양한 형식화 옵션을 제공하므로 원하는 형태로 출력가능.
    • null을 처리할 수 없으며, 정수 값이 null인 경우 NullPointerException이 발생
    • 예시: String str = String.format("%d", number);
  4. DecimalFormat 클래스:
    • DecimalFormat 클래스를 사용하여 정수를 형식화된 문자열로 변환
    • 형식화된 출력을 위한 유연한 형식 옵션 제공
    • null을 처리할 수 없으며, 정수 값이 null인 경우 NullPointerException이 발생
    • 예시: DecimalFormat decimalFormat = new DecimalFormat("#,###"); String str = decimalFormat.format(number);

결론, 여기서는 String.valueOf()가 맞을것같다. null을 "null"문자열로 변환하는 조건도 일치하며 예외가 발생되지 않기 때문에

 

1-3. 배열로 받는 경우의 수 구현

private String arrayToString(Object[] data) {
    StringBuilder sb = new StringBuilder();
    String result = "";

    if(data.length == 0) return "";

    for(Object con : data){
      sb.append(stringify(con));
      sb.append(",");
    }

    result = sb.substring(0,sb.length()-1).toString();
    return String.valueOf(result);
  }

* 반복문으로 갈 수록 concat 또는 + 연산자 보다 stringBuilder의 append 연산자가 더 유리하다.

 

chat GPT에서...

+ 연산자를 사용하는 것은 간단하고 직관적인 방법입니다. 예를 들어, "Hello, " + name + "!와 같은 형태로 문자열을 합칠 수 있습니다. 하지만 + 연산자는 내부적으로 StringBuilder 객체를 생성하고 append() 메서드를 호출하여 문자열을 합치는 작업을 수행합니다. 따라서 + 연산자를 반복적으로 사용하면 성능 저하가 발생할 수 있습니다. 특히 반복문 안에서 문자열을 합치는 경우에는 성능 문제가 더욱 심각해질 수 있습니다.

append() 메서드는 StringBuilder 클래스의 인스턴스를 사용하여 문자열을 합칩니다. StringBuilder는 가변 크기의 문자열을 효율적으로 처리하기 위해 설계된 클래스로, 내부적으로 버퍼를 사용하여 문자열을 저장합니다. append() 메서드는 문자열을 기존 버퍼에 추가하는 방식으로 작동하므로 + 연산자보다 효율적입니다. 따라서 반복적으로 문자열을 합칠 때는 append() 메서드를 사용하는 것이 성능적으로 유리합니다.

 

stringBuilder vs string Buffer

toString() 메서드는 StringBuilder 클래스에도 있으며, 동일한 방식으로 사용할 수 있습니다. StringBuffer와 StringBuilder는 유사한 기능을 제공하지만, StringBuffer는 스레드 안전(thread-safe)하고 동기화되어 있으므로, 멀티스레드 환경에서 사용할 때 선호됩니다. 단일 스레드 환경에서는 StringBuilder를 사용하는 것이 성능상 이점이 있습니다.

1-4 해시맵으로 받는 함수 구현

public String mapToString(HashMap<?,?> data){
    //LinkedHashMap Map은 순서가 없는데 순서 유지해서 복사함.
    HashMap<?,?> map = new LinkedHashMap<>(data);
    //순서가 없으니 키셋으로 받아온다음 순서가 있는 배열로 변환함.
    Object key = map.keySet().toArray()[0];
    //배열 첫번째꺼 빼면서 키값으로 value 값에 접근
    Object value = map.get(key);

    StringBuilder sb = new StringBuilder();

    //문자열이면 "" , 숫자면 그냥 그대로 가지고오는 함수 호출
    sb.append(stringify(key));
    sb.append(":");
    sb.append(stringify(value));
    String result = sb.toString();

    // 사이즈가 1개면 , 필요없으므로 그냥 그대로 출력
    if(map.size()==1) {
      //썼던 키값은 빼줘야 순차적으로 접근 가능함.
      map.remove(key);
      return result;
    }
    // 사이즈가 여러개이면 , 필요함
    else{
      //썼던 키값은 빼줘야 순차적으로 접근 가능함.
      map.remove(key);

      //재귀함수로 반복
      return result + "," + mapToString(map);
    }
}

두가지 방법으로 풀 수 있다.

1) hashmap은 원래 순서가 없다 => LinkedHashMap 맵의 순서대로 나오는 자료구조 활용

keyset 담고 첫번째 키배열 빼주고 리턴 그다음 돌대 또 첫번째 키배열 빼주고 리턴.

2) entryset 이용 : 모든 엔트리를 셋으로 리턴해줌. => 반복문 돌면서 글자 찍어줌.

 

끝.