Spring

GSON toJson, fromJson에 대한 ...삽질?!

작심평생 2020. 7. 25. 23:57
가변적(key, value변수가 어떤게 들어올지 모름..)으로 넘어오는 JSON 데이터가 있으며 특징으로는
upperCase에 underscope으로 Key값이 이뤄져있다. (이건 불변)
 
이때 카멜케이스로 만들어놓은 VO에 setting하는 과정 or Map으로 받는 과정에서 나름(...) 고민해본 흔적을 남기고자 한다...
 

 

[ FieldNamingStrategy 를 커스텀 하자! ]


    - toJson
        FieldNamingStrategy을 커스텀해서 내가 작성한 규칙으로 키값이 변경된 JSON 키값으로 변경 가능 


    - fromJson
        역직렬화 시 적용이 안되는데 이유를 모르겠다..

        ex. {“UP_DOWN" : “하하”} 인 경우, 맵핑하고자 하는 Class의 필드값이 upDown인 경우 맵핑이 안됨
        이때는 @SerializedName 이용

        @SerializedName(“UP_DOWN")
        private String upDown;

단점으로는….필드값이 많아지면….하나씩 선언해줘야할..꺼같다..(끔찍)
 


[ 상세 코드]

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
32
33
34
35
36
37
import com.google.gson.FieldNamingStrategy;
 
import java.lang.reflect.Field;
 
public class customNaming implements FieldNamingStrategy { // 커스텀 필드네이밍 전략 클래스
 
    // 대문자 언더스콥 -> 카멜케이스로
    @Override
    public String translateName(Field f) {
        int cnt = 0;
        StringBuilder tmpStr = new StringBuilder();
 
        String keyVal = f.getName(); // Json 데이터 키값 가져옴
        String[] splitVal = keyVal.toLowerCase().split("_");
 
        for (String str : splitVal) {
            if (cnt == 0) {
                tmpStr.append(str);
                cnt++;
            } else {
                str = str.trim();
                StringBuilder stringBuilder = new  StringBuilder(str);
                stringBuilder.setCharAt(0,  Character.toUpperCase(str.charAt(0))); // 맨 앞 한글자만 대문자로
                String afterStr = stringBuilder.toString();
                tmpStr.append(afterStr);
            }
        }
        return tmpStr.toString();
    }
//------------------------------------------
// 호출 부분
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingStrategy(new customNaming());
 
Gson gson = gsonBuilder.create();
testVO testVO = gson.fromJson(str, testVO.class);
 
 

 
 
넘어오는 JSON데이터의 키값이 가변적인 경우
VO에 다 맵핑할 수 없다. 따라서 맵으로 받는 방식
 
[ 제이슨 String 데이터 -> 링크드해쉬맵(키값이 카멜 적용이된) 으로 변환 (순서성 지키기위해) ]

  • 상황설명
    • 뷰에서 Ajax를 이용하여 목록 클릭 시 테이블이 있는 레이어 팝업을 띄우는 상황
    • 데이터는 DB에서 조회함. TH 데이터(코드값과 코드값이 의미하는 한글명)와 JSON 형식의 문자열이 저장되어있다.
    • TH_CODE 값과 JSON 데이터의 키값이 다른 경우는 없다.
    • 테이블에 뿌려줄때 각 항목이 일치하게 뿌려줘야 한다.
[ 테이블 Th에 뿌려줄 데이터 DB 조회 결과]

  • 불특정 시기에 DB의 데이터들이 언제 UPDATE, INSERT, DELECT 될지 모르는 상황
TH_CODE
TH_NAME
UP_DOWN
제목
 
[ 백단으로 넘어온 JSON 데이터 DB 조회 결과]
  • JSON 데이터 내의 순서는 랜덤. (어느값이 먼저올지 모름)
  • 문자열로 된 json data이다.
{“USE_YES_NO” :”NO”, “UP_DOWN” : “DOWN" }
 
[ 상세 코드]


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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
    // 테이블 컬럼은 안 바뀌는데 VO를 안쓰고 왜 굳이 Map을 쓴건지?
    //  - 순서성이 중요하기 때문에 DB조회 순서로 그대로 가져오기 위해 LinkedHashMap 이용
    List<LinkedHashMap<String, Object>> thList = new ArrayList<LinkedHashMap<String, Object>>();
    List<LinkedHashMap<String, Object>> jsonList = new ArrayList<LinkedHashMap<String, Object>>();
 
    String keyVal = ""// JSON 데이터가 있는 쿼리 DB조회 후 컬럼값을 담을 변수
    String makeJsonStr = "{"//json 형식으로 문자열 만들어줄 변수
    String jsonKeyStr = ""// JSON 데이터의 키값을 담을 변수
 
    for(Map<String, Object> vo : resultList){ // resultList : 한 컬럼에 제이슨 형식의 문자열이 들어있다. 또한 ROW가 여러줄이기 때문에 List<Map> 형태로 가져온다
        Iterator<String> itr = vo.keySet().iterator(); // 키 리스트 (== 컬럼 이름값들이 들어 있을 것)
        while(itr.hasNext()){
            keyVal = itr.next();
            if (!keyVal.equals("JSON_DATA")) { // 제이슨 데이터가 들어있는 컬럼이름이 아닌 경우
                continue;
            }
            JSONObject obj = new JSONObject(vo.get(keyVal)); // JSONOBJECT : JSON 문자열 형태를 KEY : VALUE 형태로 형 변환
            Iterator<String> itr2 = obj.keys();
 
            while(itr2.hasNext()){
                jsonKeyStr = itr2.next();
                makeJsonStr += "\'"+jsonKeyStr + "\' : \'" + obj.getString(jsonKeyStr) + "\',"// 카멜케이스를 적용하려면 jsonKeyStr 대신 JdbcUtils.convertUnderscoreNameToPropertyName(jsonKeyStr) 이용
 
            }
            makeJsonStr = makeJsonStr.substring(0,makeJsonStr.lastIndexOf(","))+"}";
 
            LinkedHashMap<String, Object> jsonVo = new Gson().fromJson(makeJsonStr, new TypeToken<LinkedHashMap<String, Object>>(){}.getType());
 
            // th 코드값과 map으로 변환된 json 카값을 맞는 값을 맵핑하는 과정
            // th 코드값기준으로 키값 비교 돌리는 중
            LinkedHashMap<String, Object> finalLinkMap = new LinkedHashMap<>();
            for(Map<String, Object> thMap : resultTh){ // resultTh : th의 코드와 한글 이름값을 가져온 쿼리. List<Map> 형태
                Iterator<String> itr3 = thMap.keySet().iterator();
                while(itr3.hasNext()){
                    String keyVal2 = itr3.next();
                    if (keyVal2.equals("TH_CODE")) { // th코드값이 들어있는 컬럼 이름인 경우
                        String seachKeyVal = String.valueOf(thMap.get(keyVal2)); // th코드값 할당
                        finalLinkMap.put(seachKeyVal, jsonVo.get(seachKeyVal)); // th코드값, th코드값에 해당하는 JSON value 값
                    }
                }
            }
            jsonList.add(finalLinkMap);
            makeJsonStr = "{"// 초기화
        }
    }
    map.put("jsonList", jsonList);
 
    return new Gson().toJson(map);
}