[공공 API 호출 및 가공]

곽동현·2024년 3월 4일
0
  1. 공공 API 를 규격에 맞게 API를 호출하고 , Json형식 String 으로 저장한다.
 // 1. URL을 만들기 위한 StringBuilder
        StringBuilder urlBuilder = new StringBuilder("http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getCtprvnRltmMesureDnsty"); /*URL*/

        // 2. 오픈 API의 요청 규격에 맞는 파라미터 생성
        urlBuilder.append("?" + URLEncoder.encode("serviceKey","UTF-8") + "=ebZTQ4iRgDlaQNNtqJ5A9RcdWIhNzGvnQFQxLCwXDgjX/pNKJ49cQjal7Otx6mIyZpr/GWQWvwieBTwSMcb40w=="); /*Service Key*/
        urlBuilder.append("&" + URLEncoder.encode("returnType","UTF-8") + "=" + URLEncoder.encode("json", "UTF-8")); /*xml 또는 json*/
        urlBuilder.append("&" + URLEncoder.encode("numOfRows","UTF-8") + "=" + URLEncoder.encode("100", "UTF-8")); /*한 페이지 결과 수*/
        urlBuilder.append("&" + URLEncoder.encode("pageNo","UTF-8") + "=" + URLEncoder.encode("1", "UTF-8")); /*페이지번호*/
        urlBuilder.append("&" + URLEncoder.encode("sidoName","UTF-8") + "=" + URLEncoder.encode("서울", "UTF-8")); /*시도 이름(전국, 서울, 부산, 대구, 인천, 광주, 대전, 울산, 경기, 강원, 충북, 충남, 전북, 전남, 경북, 경남, 제주, 세종)*/
        urlBuilder.append("&" + URLEncoder.encode("ver","UTF-8") + "=" + URLEncoder.encode("1.0", "UTF-8")); /*버전별 상세 결과 참고*/

        // 3. URL 객체 생성 (String으로 변환)
        URL url = new URL(urlBuilder.toString());

        // 4. 요청하고자 하는 URL과 통신하기 위한 Connection 객체 생성
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

        // 5. 통신을 위한 메소드 세팅 ("GET")
        conn.setRequestMethod("GET");

        // 6. 통신을 위한 Content-type 세팅 ("JSON" 으로 해야함 - reuslt의 값은 String이기 때문에)
        conn.setRequestProperty("Content-type", "application/json");
        // 7. 통신 응답 코드 확인
        System.out.println("Response code: " + conn.getResponseCode());

        // 8. 전달받은 데이터를 BufferedReader 객체로 저장. 오류가 날 경우 error발생
        BufferedReader rd;
        if(conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
            rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        } else {
            rd = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
        }

        // 9. 저장된 데이터를 라인별로 읽어 StringBuilder 객체로 저장
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = rd.readLine()) != null) {
            sb.append(line);
        }
        // 10. 객체 해제
        rd.close();
        conn.disconnect();

우리는 그럼 이제 무엇을 해야할까?

적절한 데이터로 가공하고 사용하기 위해 DTO 로 개발!

즉, JSON Parsing 과정이 필요하다.

이걸 왜 파싱해?간단하죠. 저희는 이렇게 JSON형태로 온 데이터들을 파싱, 음..데이터를 뽑아낸다고 말하는게 좋을까요?저 형태로는 저 데이터들을 이용할 수 없으니까요. 우리는 저기서 데이터들을 뽑아서 아 이름이 테스트고, 나이가 25고.. 배열 또는 딕셔너리 형태로 저장해야 저 데이터들을 쓸 수 있겠죠?그래서 다들 JSON 파싱 파싱 그러는거랍니다. 
출처: https://zeddios.tistory.com/90 [ZeddiOS:티스토리]

        String jsonString = sb.toString();
        
        // 11. 결과 출력
//        System.out.println(jsonString);
        log.warn("진입1");
        log.info(jsonString);

        // 12. 해야할 것 -> 데이터 사용하려면 적절하게 가공해야함
        // 입력이 Json 형테의 String으로 들어오는 상황. -> Json파싱 필요
        // JSONParser, gson, ObjectMapper등을 사용하여 변환가능.

        jsonParser2(jsonString);

    public void jsonParser2(String jsonString) throws ParseException {
        log.info("진입2");
        // 1. 문자열 형태의 JSON을 파싱하기 위한 JSONParser 객체 생성
        JSONParser parser = new JSONParser();
        // 2. 문자열을 JSON 형태로 JSONObject 객체에 저장
        JSONObject obj = (JSONObject)parser.parse(jsonString);
        // 3. 필요한 리시트 데이터 부분만 가져와 JSONArray로 저장
        JSONArray dataArr = (JSONArray)obj.get("data");
        // 4. 출력 확인
        System.out.println(dataArr.toString());
    }

해당 과정을 했을때의 에러코드이다.

잘보면 String 으로는 잘 들어왔다.
그러나 dataArr에는 null이 들어왔단다.
이유가 뭘까?

https://velog.io/@yeonn1006/Cannot-invoke-org.json.simple.JSONArray.size-because-array-is-null

바로 제공되는 step에 따라 Mapping을 해주지 않았기때문!
우리는 제공해주는 Data의 스텝을 지켜주면서 데이터를 받아와야만한다.

해당 부분을 고쳐서 해보자.

        try{
            // 1. 문자열 형태의 JSON을 파싱하기 위한 JSONParser 객체 생성.
            JSONParser parser = new JSONParser();
            // 2. 문자열을 JSON 형태로 JSONObject 객체에 저장.
            JSONObject obj = (JSONObject)parser.parse(jsonString);

            JSONObject responseResult = (JSONObject)obj.get("response");
            JSONObject headerResult = (JSONObject)responseResult.get("header");
            JSONObject bodyResult = (JSONObject)responseResult.get("body");
            JSONObject itemsResult = (JSONObject)bodyResult.get("items");
            JSONArray itemResult = (JSONArray) itemsResult.get("item");

            for (Object item : itemResult) {
                log.info(item.toString());
            }

        } catch (ParseException e) { System.out.println(e.getMessage()); }

잘 될줄 알았는데 이게 웬걸.

난데없는 아래와 같은 에러가 발생했다.
에러를 살펴보니 JSONArray는 Object로 캐스팅 할 수 없단다!

java.lang.ClassCastException: class org.json.simple.JSONArray cannot be cast to class org.json.simple.JSONObject

데이터가 잘못됐나? 찾아보니,

여기서 xml 데이터로 확인했을 때, items 라는 Object 안에item배열이 있는 줄 알았다.

해당 Open API 데이터는 items 라는 배열안에 item Object들이 모여있었다!
즉, 배열 안에 오브젝트가 있다는 것.

        try{
            // 1. 문자열 형태의 JSON을 파싱하기 위한 JSONParser 객체 생성.
            JSONParser parser = new JSONParser();
            // 2. 문자열을 JSON 형태로 JSONObject 객체에 저장.
            JSONObject obj = (JSONObject)parser.parse(jsonString);

            JSONObject responseResult = (JSONObject)obj.get("response");
            JSONObject headerResult = (JSONObject)responseResult.get("header");
            JSONObject bodyResult = (JSONObject)responseResult.get("body");
            JSONObject itemsResult = (JSONArray)bodyResult.get("items");	//Object가 아닌, Array
            
            // JSONArray itemResult = (JSONArray) itemsResult.get("item"); 해당부분 제거


			// Array속 Object를 꺼내오려면 이렇게 ! 
            for(int i=0; i<itemsResult.size(); i++){
                JSONObject getItem = (JSONObject) itemsResult.get(i);	//Object 추출
                log.info((String)getItem.get("pm10Value"));
            }

        } catch (ParseException e) { System.out.println(e.getMessage()); }

https://velog.io/@tmdgk4902/공공데이터-포털-오픈-API-받아오기-파싱

profile
실패의 경험들을 채워나가기!

0개의 댓글