본문 바로가기
Web Development/Spring Legacy

[Spring Legacy / Java / Javascript] AJAX로 전달한 JSONArray를 Controller(서버단)에서 파싱하기 A to Z (feat. Generic Type, JSONObject 파싱)

by 감자맹고우 2022. 4. 21.
728x90
반응형

프로젝트에서 Javascript에서 JSON 형식으로 구성된 객체를 Array로 만들어 Controller에서 받아 처리하는 기능을 구현하게 되었다.

예를 들면, [ {num : 1, score : 90}, {num : 2, score : 80}, {num : 3, score : 70} ] 과 같은 형태의 데이터이다.

AJAX로 data를 넘길 때 해당 배열 데이터를 넘기고, Controller에서 파라미터에 JSONArray 타입으로 받도록 하면 되지 않을까 싶지만, 아쉽게도 Spring이 거기까지 처리해주진 못한다.

 

그래서 오늘은 JSONArray 형식의 데이터를 서버단에서 받을 수 있게 처리하는 방법을 공유하고자 한다.

추가적으로, 제너릭 타입(Generic Type)을 이용해 JSONArray에서 JSONObject를 파싱할 때 (String) jsonArray.get(0) 과 같이 형변환하는 코드를 줄일 수 있도록 리팩토링까지 진행해보고자 한다.

 

 

[ 적용 방법 ]

 

우선, 이 글에서 적용하는 방법은 JSONArray 형식의 데이터를 String으로 처리하여 컨트롤러로 넘기고, 컨트롤러에서 파싱하는 방법이니 참고바란다.

 

1. traditional 옵션 설정, JSON.stringify로 Javascript 객체를 JSON 문자열로 변환하여 전달

 

// HTML
<button type="button" onclick="test();">
    테스트
</button>

// Javascript
const test = () => {
    
    //전송할 샘플 JSONArray 형식 데이터
    const testArr = [{num:1, score:90}, {num:2, score:80}, {num:3, score:70}];
    
    $.ajax({
        type: "POST",
        //Array 형식의 데이터를 전송할 때는 traditional: true 옵션을 적용
        traditional: true,
        url: "/ajaxTest",
        //JSON.stringify를 통해 Javascript 객체를 JSON 문자열로 변환
        data: {data : JSON.stringify(testArr)},
        success: function (data) {
            console.log(data);		        	
        },
        error: function () {
            alert('오류 발생');
        }
    });
}

 

2. Controller 에서 String으로 받아 파싱

 

//Java
//Controller

//import하기 위해서는 pom.xml에 json-simple, jackson 의존성 추가 필요
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;

@Controller
public class TestController {

    @ResponseBody
    @RequestMapping("/ajaxTest")
    public JSONArray ajaxTest(String data){
        //data : JSON.stringify(testArr)
        //[{num:1, score:90}, {num:2, score:80}, {num:3, score:70}]
    
        JSONArray jsonArr = new JSONArray();
        ObjectMapper mapper = new ObjectMapper();
    
        try { 
            //JSONArray 타입으로 파싱
            jsonArr = mapper.readValue(data, JSONArray.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    
        return jsonArr;
    }

}

 

이렇게 하면, Javascript에서 보낸 JSONArray 타입 형태의 데이터를 컨트롤러에서 JSONArray로 파싱할 수 있다!

 

반응형

 

하지만, 이렇게 끝나면 아쉽다.

그래서 추가적으로 JSONArray를 JSONObject로 파싱하고 내부 데이터를 조회하는 코드와 리팩토링까지 진행해보려고 한다.

 

1. 오류 발생 코드

 

//Java
//Controller

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;

@Controller
public class TestController {

    @ResponseBody
    @RequestMapping("/ajaxTest")
    //int 로 반환 타입 변경
    public int ajaxTest(String data){
        //data : JSON.stringify(testArr)
        //[{num:1, score:90}, {num:2, score:80}, {num:3, score:70}]
    
        JSONArray jsonArr = new JSONArray();
        ObjectMapper mapper = new ObjectMapper();
    
        try {
            jsonArr = mapper.readValue(data, JSONArray.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    
        //========= JSONObject Parsing =========//
        JSONObject jobj = new JSONObject();
        
        //예시로 쓸 JSONArray 첫번째 JSONObject 할당
        for(int i = 0; i < jsonArr.size(); i++){
            jobj = (JSONObject) jsonArr.get(i);
            break;
        }
        
        //JSONObject 조회
        int num = 0;
        if(jobj != null) num = (int)jobj.get("num");
        
        //첫번째 JSONObject인 {num:1, score:90}의 num 키 값을 조회하였으므로, num = 1
        return num;
    }

}

 

위와 같이 JSONArray를 JSONObject로 바로 형변환해서 처리하려고 하다보면 이런 형변환 관련 에러들을 만날 수 있다.

 

java.util.hashmap cannot be cast to org.json.simple.jsonobject
org.json.simple.jsonobject cannot be cast to java.lang.integer

cf) org.json.simple.jsonobject cannot be cast to java.lang.string (위 코드와는 별개의 에러)

 

 

2. 파싱 메소드

 

외부 API를 이용하거나 다른 사례에서도 json 형식의 data가 hashmap으로 처리되어 종종 비슷한 오류들을 만나곤 한다. 이럴 때 유용하게 사용할 수 있는 메소드를 작성해보았다.

 

//Java
public <T extends Object> T parseJSONData(HashMap<String, String> object, String name, Class<T> type) throws JsonProcessingException, ParseException {
    //Object를 상속받은 Generic Type으로 return
    //이 때 T 타입은 파라미터의 type으로 설정
    
    JSONParser parser = new JSONParser();
    ObjectMapper mapper = new ObjectMapper();

    if(object != null) {
        //파라미터의 name 을 키 값으로 조회
        if(object.get(name) != null) return type.cast(parser.parse(mapper.writeValueAsString(object.get(name))));
    }
    
    //HashMap<String, String> object 가 null이면 null을 리턴
    return null;
}

 

이런식으로 JSONArray의 요소를 JSONObject로 형변환없이 HashMap형태로 처리하고 조회할 키값과 결과 값의 타입을  지정하여 바로 처리해줄 수 있다. 타입은 Generic Type으로 처리하여 별개의 형변환 코드가 필요 없어 간결하다.

 

 

3. 적용

 

//========= JSONObject Parsing =========//
//예시로 쓸 JSONArray 첫번째 JSONObject 할당
for(int i = 0; i < jsonArr.size(); i++){
    //예시이므로, 첫번째 인덱스에서 리턴하도록 처리하였음
    //{num:1, score:90}의 num 키 값을 조회하였으므로, num = 1
    return parseJSONData((HashMap<String, String>)jsonArr.get(i), "num", Integer.class));
}

 

즉, 1번 오류 발생 코드에서 return 부분을 위와 같은 간결한 코드로 처리할 수 있는 것이다. 이렇게 하면 형변환으로 인한 에러를 최소화할 수 있다!

(만약 에러가 난다면 parseJSONData의 타입을 String.class로 지정 후 Integer.parseInt로 형변환하면 된다)

 

🤞 도움이 되셨기를 바랍니다. 한 번의 클릭과 댓글은 어딘가의 누군가에게 진실로 큰 힘이 됩니다. 🐱‍🏍

 

728x90
반응형

댓글