자바

[Java] JSON Parsing

Atriel 2023. 12. 19. 15:06

 

 

[파싱할 데이터]

 

영화진흥위원회 오픈API

제공서비스 영화관입장권통합전산망이 제공하는 오픈API서비스 모음입니다. 사용 가능한 서비스를 확인하고 서비스별 인터페이스 정보를 조회합니다.

www.kobis.or.kr

 

 

API를 얻어오기 위해서 간단하게 회원가입 후 키를 발급 받자

 

그리고 JSON 데이터를 확실하게 확인하기위해 크롬 확장 프로그램을 받음

 

JSON Formatter

Makes JSON easy to read. Open source.

chrome.google.com

 

 

 

REST란

URL을 통해서 데이터에 접근하는거라 생각하면 편함

 

SOAP 방식도 있는데 좀 옛날 방식 (WSDL에서 분석해서 요청URL에서 가져온다)

잘 사용하지 않음 (복잡함)

 

 


REST방식의 요청 URL을 긁어오자

 

만약 긁어온 링크를 시크릿 탭에서 그냥 열어보면

 

 

 

이런식으로 키 값이 맞지 않는다고 뜬다

 

위 사이트에서 제공하는 요청 인터페이스를 보면

 

 

코드를 치기전에 위 인터페이스를 사용하여 실제 데이터를 먼저 가져와야 한다.

 

긁어온 링크

[xml]

http://www.kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.xml

[Json]

http://www.kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.JSON

 

뒤에 키값을 붙여주자

실제 키값을 티스토리 공개글에 쓸수는없으니

임시로 a123456789 라고 가정하고 써봄

 

위의 링크 뒤에 ?을 붙이고 키값을 넣어주겠다

[JSON]

http://www.kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json? key=a123456789&targetDt=20231120

 

이런식으로 시작은 ? 그뒤에 오는값은 &을 이용하여 추가하면 된다.

 

 

크롬에 붙여보면 이런식으로 데이터를 나오는데 이를 파싱하면 된다

 

XML Parsing 방법

SAX같은 방식은 너무 오래된 구식 방식이고...

자바의 DocumentBuilderFactory를 이용하여 읽어보자

 

Java에서 XML 파일을 읽는 방법

이 게시물은 Java에서 XML 파일을 읽는 방법을 소개합니다.

www.delftstack.com

 

 

Java - JSON을 파싱하는 가장 쉬운 방법

org.json 라이브러리를 사용하여 JSON을 파싱하는 방법을 소개합니다. JSON은 Object, Array, Key-Value 타입으로 이루어져 있으며 Value는 String, Int, Long, Boolean 등의 타입을 지원합니다.

codechacha.com

 

 

[BoxOfficeOpenApi.java]

package ex01.parsing;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class BoxOfficeOpenApi {

	public static final String KEY = "4298055d882fbc5c120b654c1fa42815";
	public static final String WEEKLY_BOXOFFICE_XML_URL = "http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchWeeklyBoxOfficeList.xml";
	public static final String WEEKLY_BOXOFFICE_JSON_URL = "http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchWeeklyBoxOfficeList.json";
	
	public static void main(String[] args) {
//		parseWeeklyBoxOfficeByXML("20231126");
		parseWeeklyBoxOfficeByJson("20231126");
		
	}
	
	
	// https://www.delftstack.com/ko/howto/java/java-read-xml/
	public static void parseWeeklyBoxOfficeByXML(String targetDt) {
		try {
			// 1. URL 가공 코드
			StringBuilder urlBuilder = new StringBuilder(WEEKLY_BOXOFFICE_XML_URL);
			urlBuilder.append("?" + "key"+ "=" + KEY);
			urlBuilder.append("&" + "targetDt"+ "=" + targetDt);
			urlBuilder.append("&" + "weekGb"+ "=" + 1); // 주말만 보는 옵션
			
			// URLEncoder.encode : 특정 char-set으로 인코딩 하는 함수, 주로 UTF-8을 사용, UTF-8일 경우 영문은 안해도 무관!
			//                     한글일 경우는 반드시 UTF-8 포멧팅하는 것을 권장
//			urlBuilder.append("&" + URLEncoder.encode("pageNo","UTF-8") + "=" + URLEncoder.encode("1", "UTF-8")); // 과한 인코딩 예시
//			urlBuilder.append("&" + "name"+ "=" + URLEncoder.encode("홍길동", "UTF-8"));
			System.out.println(urlBuilder);
			
			// 2. URL을 통해 HTML을 요청하는 코드를 작성
			URL url = new URL(urlBuilder.toString()); // url 객체 생성 
			HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // URL을 통해서 Http 연결을 요청
			conn.setRequestMethod("GET"); // get방식으로 요청을 알리는 코드
			
			// API마다 요청에 대한 property 셋팅이 달라지는 코드
			// 아래 4가지 중에서 골라서 선택을 해야하는데, 안될수도 있으니 정확한 방법은 API 예제 코드를 참고하거나 문서 참고 
//	        conn.setRequestProperty("Content-type", "application/json");
//	        conn.setRequestProperty("Content-type", "application/xml");
	        conn.setRequestProperty("Accept", "application/xml");
//	        conn.setRequestProperty("Accept", "application/json");
	        System.out.println("Response code : " + conn.getResponseCode()); // 실제 url로 부터 페이징을 요청하는 단계
	        
	        // 3. Response code 여부를 판단하고 파싱을 시작하는 코드를 작성
	        if(conn.getResponseCode() < 200 || conn.getResponseCode() >=300) {
	        	System.out.println("페이지를 찾을수 없습니다.");
	        	return;
	        }
	        
	        // 4. XML 파싱 시작, DocumentBuilderFactory 활용 (1.8부터 가능!)
	        // Document : 문서, 태그(트리) 상태의 객체 
	        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	        DocumentBuilder db = dbf.newDocumentBuilder();
	        Document doc = db.parse(conn.getInputStream()); // html문서를 가져와서 xml 파싱 준비 완료!
	        doc.normalizeDocument(); // xml을 표준으로 다시 정리해주는 기능 -> 해도 되고 안해도 될수도 있음. 
	        // 셋팅부 끝
	        
	        // 본격적인 파싱 코드 시작 
	        System.out.println("Root element(=tag) : " + doc.getDocumentElement().getNodeName());
	        System.out.println("----------------------------------------------------------------");
	        
	        // 자식 node 접근하는 방법 -> 트리 순회하는 방법이 필요
	        // -> 추천하지 않는 방법 
	        System.out.println(doc.getDocumentElement().getChildNodes().item(0).getTextContent());
	        System.out.println(doc.getDocumentElement().getChildNodes().item(1).getTextContent());
	        System.out.println(doc.getDocumentElement().getChildNodes().item(2).getTextContent());
	        
	        // getElementsByTagName를 통해 순회하는 것을 추천 ★★★★★
	        // ※ 주의 : 파싱 대상이 되는 문자열은 대소문자 구별됨, 반드시 올바른 대소문자 사용할것
	        // getElementsByTagName : 태그 이름으로부터 node 가져오는 기능, node안에 node 있을수 있는 구조, Tree구조
	        //                        장점 : 자식 부모 상관 없이 tag 이름을 통해서 node 가져올수 있음.
	        //                        s가 붙는 경우 list로 반환됨
//	        <tag-name>content</tag-name>
	        System.out.println("boxofficeType : " + doc.getElementsByTagName("boxofficeType").item(0).getTextContent());
	        System.out.println("showRange : " + doc.getElementsByTagName("showRange").item(0).getTextContent());
	        System.out.println("yearWeekTime : " + doc.getElementsByTagName("yearWeekTime").item(0).getTextContent());
	        
	        NodeList boxOfficeList = doc.getElementsByTagName("weeklyBoxOffice");
	        for(int i = 0; i < boxOfficeList.getLength(); i++) {
	        	Node node = boxOfficeList.item(i);
	        	System.out.println("\nCurrent node : " + node.getNodeName());
	        	if(node.getNodeType() == Node.ELEMENT_NODE) { // ELEMENT_NODE 배열이 아닌 일반노드 일 때
	        		Element e = (Element)node;
	        		System.out.println("rank : " + e.getElementsByTagName("rank").item(0).getTextContent());
	        		System.out.println("movieNm : " + e.getElementsByTagName("movieNm").item(0).getTextContent());
	        		System.out.println("openDt : " + e.getElementsByTagName("openDt").item(0).getTextContent());
	        		System.out.println("audiAcc : " + e.getElementsByTagName("audiAcc").item(0).getTextContent());

	        		// 가끔 누락된 데이터 값의 예시
	        		// -> 누락된 데이터는 skip할수 있도록 try-catch로 감싼다.
	        		try {
	        			System.out.println("error : " + e.getElementsByTagName("error").item(0).getTextContent());
					} catch (Exception e2) {
//						e2.printStackTrace();
					}
	        		
	        		// 위와 같이 try-catch문으로 도배가 되는 경우 코드가 난잡해짐으로 메소드로 정리해서 빼준다.
	        		String str = getString(e, "error");
	        		System.out.println("error : " + str);
	        		System.out.println();
	        	}
	        }
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static String getString(Element e, String name){
		try {
			return e.getElementsByTagName(name).item(0).getTextContent();
		} catch (Exception e2) {}
		return null;
//		return "";
//		return "-";
	}
	
	// https://codechacha.com/ko/java-parse-json/
	public static void parseWeeklyBoxOfficeByJson(String targetDt) {
		try {
			// 1. URL 가공 코드 시작
			StringBuilder urlBuilder = new StringBuilder(WEEKLY_BOXOFFICE_JSON_URL);
			urlBuilder.append("?" + "key" + "="+KEY); 
			urlBuilder.append("&" + "targetDt" + "=" + targetDt); 
			urlBuilder.append("&" + "weekGb"+ "=" + 1); // 주말만보는 옵션 
			System.out.println(urlBuilder);
	    
			// 국문 키워드가 섞인 경우에는 아래와 같이 URLEncoder.encode 사용이 필수, 단 영어+숫자만 있으면 사용 안해도 무관
//			urlBuilder.append("?" + "serviceKey" + "=서비스키"); /*Service Key*/
//	        urlBuilder.append("&" + URLEncoder.encode("pageNo","UTF-8") + "=" + URLEncoder.encode("1", "UTF-8")); /*페이지번호*/
//	        urlBuilder.append("&" + URLEncoder.encode("numOfRows","UTF-8") + "=" + URLEncoder.encode("1000", "UTF-8")); /*한 페이지 결과 수*/
			
			// 1. URL 가공 코드 끝
	        
	        // 2. URL을 HTTP 객체를 통해 요청하는 코드 시작
	        URL url = new URL(urlBuilder.toString()); // URL 객체 생성
	        HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // URL을 HTTP를 요청할 객체를 다시 생성
	        conn.setRequestMethod("GET"); // get 방식으로 요청하는 코드

	        // API 마다 요청하는 방법 이달라짐 대략적으로 아래 2개 방식을 선택하면 보통 호출 가능
//	        conn.setRequestProperty("Content-type", "application/json");
//	        conn.setRequestProperty("Content-type", "application/xml");
	        conn.setRequestProperty("Accept", "application/xml");
//	        conn.setRequestProperty("Accept", "application/json");
	        
	        System.out.println("Response code: " + conn.getResponseCode()); // 실제 HTTP로 호출을 시도하는 코드
	        // 2. URL을 HTTP 객체를 통해 요청하는 코드 끝
	        
	        // 3. Response code를 통해 성공 여부를 판단하고 파싱을 시작하는 코드
	        //    성공은 200번 대
	        if(conn.getResponseCode() < 200 || conn.getResponseCode() >= 300) {
	        	System.out.println("페이지가 잘못되었습니다.");
	        	return;
	        } 
	        
	        // 4. Json 파싱부 시작
	        // JSONObject : Key-value로 구성된 JS객체를 의미
	        // JSONArray : Json배열로 구성된 객체를 의미
	        InputStream is = conn.getInputStream();
	        InputStreamReader isr = new InputStreamReader(is, "UTF-8");
	        BufferedReader br = new BufferedReader(isr);
	        
	        // JSONParser : 트리 구조로 순회해야한다. 
	        JSONParser jsonParser = new JSONParser();
	        JSONObject rootObj = (JSONObject) jsonParser.parse(br); // root라 데이터도 없는 곳
	        JSONObject childObj = (JSONObject) rootObj.get("boxOfficeResult"); // 데이터가 존재하는 곳
	        
	        // get : key값으로 value를 가져오는 메소드
	        // Root의 자식 출력
	        System.out.println("boxofficeType : " + childObj.get("boxofficeType"));
	        System.out.println("showRange : " + childObj.get("showRange"));
	        System.out.println("yearWeekTime : " + childObj.get("yearWeekTime"));
	        System.out.println("weeklyBoxOfficeList : " + childObj.get("weeklyBoxOfficeList"));
	        System.out.println("weeklyBoxOfficeList : " + childObj.get("weeklyBoxOfficeList").getClass().getName());
	        System.out.println("--------------------------------------------------------------");
	        
	        // weeklyBoxOfficeList 반복문으로 순회하는 방법
	        JSONArray array =  (JSONArray) childObj.get("weeklyBoxOfficeList");
	        
	        for(int i = 0; i < array.size(); i++){
	        	JSONObject obj = (JSONObject) array.get(i);
	        	System.out.println("rank : " + obj.get("rank"));
				System.out.println("movieNm : " + obj.get("movieNm"));
				System.out.println("openDt : " + obj.get("openDt"));
				System.out.println("audiAcc : " + obj.get("audiAcc"));
				System.out.println("error : " + obj.get("error")); // 없을 경우 null을 반환!
				System.out.println();
	        }
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
}