XML 파싱
1. 공공데이터
1) 공공데이터란?
- 공공기관이 만들어내는 모든 공적인 정보
- 누구나 원하는 개별 키를 발급 받아 원하는 기능에 활용 가능
2) 데이터의 형태
- CSV : ","를 통해서 데이터 구분, 용량이 작지만 구조적이지 못함
- XML : 태그를 사용하여 문서 작성, 구조적, 정확한 문법이 필요, 큰 용량
- JSON : {}를 묶어 객체로 표현, 구조를 가지며 객체로 다른 언어와 호환, XML보다 비교적 저 용량
3) XML
- 마크업 언어 : 태그를 사용
- HTML과 달리 태그를 확장 가능
- 정확한 문법을 지켜야 동작
- 반드시 root element가 존재해야 한다.
- 태그는 대소문자를 구별한다.
- valid한 문서 : xml 태그는 자유롭게 생성하기 때문에 최초 작성자의 의도대로 작성되는지 확인할 필요
2. XML 파싱
1) 파싱이란
- 문서에서 필요한 정보를 얻기 위해 태그를 구별하고 내용을 추출하는 과정
- SAX parser : 문서를 읽으면서 태그의 시작, 종료 등 이벤트 기반으로 처리하는 방식
- DOM Parser : 문서를 다 읽고 난 후 문서 구조 전체를 자료구조에 저장하여 탐색하는 방식
- SAX는 빠르고 한번에 처리하기 때문에 다양한 탐색이 어렵다.
- DOM은 다양한 탐색이 가능하지만 느리고 무거우며 큰 문서를 처리하기 어렵다.
2) SAX
- 문서를 읽다가 발생하는 이벤트 기반으로 문서 처리
- MyHandler만 작성하면 됩니다.
3) DOM
- 문서를 완전히 메모리에 도링 후 필요한 내용 찾기
- 문서를 구성하는 모든 요소를 Node로 구성
- 태그들은 root 노드를 시작으로 부모-자식 관계 구성
4) JSON
- 간결한 문법, 단순한 텍스트, 적은 용량으로 대부분의 언어, 대부분의 플랫폼에서 사용 가능.
- 이 기종 간의 데이터 교환에 광범위하게 사용됨.
- 객체를 key-value의 쌍으로 관리
{
"boxOfficeResult": {
"boxofficeType": "일별 박스오피스",
"showRange": "20120101~20120101",
"dailyBoxOfficeList": [
{
"rnum": "1",
"rank": "1",
"rankInten": "0",
"rankOldAndNew": "OLD",
"movieCd": "20112207",
"movieNm": "미션임파서블:고스트프로토콜",
"openDt": "2011-12-15",
"salesAmt": "2776060500",
"salesShare": "36.3",
"salesInten": "-415699000",
"salesChange": "-13",
"salesAcc": "40541108500",
"audiCnt": "353274",
"audiInten": "-60106",
"audiChange": "-14.5",
"audiAcc": "5328435",
"scrnCnt": "697",
"showCnt": "3223"
},
{
"rnum": "2",
"rank": "2",
"rankInten": "1",
"rankOldAndNew": "OLD",
"movieCd": "20110295",
"movieNm": "마이 웨이",
"openDt": "2011-12-21",
"salesAmt": "1189058500",
"salesShare": "15.6",
"salesInten": "-105894500",
"salesChange": "-8.2",
"salesAcc": "13002897500",
"audiCnt": "153501",
"audiInten": "-16465",
"audiChange": "-9.7",
"audiAcc": "1739543",
"scrnCnt": "588",
"showCnt": "2321"
},
{
"rnum": "3",
"rank": "3",
"rankInten": "-1",
"rankOldAndNew": "OLD",
"movieCd": "20112621",
"movieNm": "셜록홈즈 : 그림자 게임",
"openDt": "2011-12-21",
"salesAmt": "1176022500",
"salesShare": "15.4",
"salesInten": "-210328500",
"salesChange": "-15.2",
"salesAcc": "10678327500",
"audiCnt": "153004",
"audiInten": "-31283",
"audiChange": "-17",
"audiAcc": "1442861",
"scrnCnt": "360",
"showCnt": "1832"
},
{
"rnum": "4",
"rank": "4",
"rankInten": "0",
"rankOldAndNew": "OLD",
"movieCd": "20113260",
"movieNm": "퍼펙트 게임",
"openDt": "2011-12-21",
"salesAmt": "644532000",
"salesShare": "8.4",
"salesInten": "-75116500",
"salesChange": "-10.4",
"salesAcc": "6640940000",
"audiCnt": "83644",
"audiInten": "-12225",
"audiChange": "-12.8",
"audiAcc": "895416",
"scrnCnt": "396",
"showCnt": "1364"
},
{
"rnum": "5",
"rank": "5",
"rankInten": "0",
"rankOldAndNew": "OLD",
"movieCd": "20113271",
"movieNm": "프렌즈: 몬스터섬의비밀 ",
"openDt": "2011-12-29",
"salesAmt": "436753500",
"salesShare": "5.7",
"salesInten": "-89051000",
"salesChange": "-16.9",
"salesAcc": "1523037000",
"audiCnt": "55092",
"audiInten": "-15568",
"audiChange": "-22",
"audiAcc": "202909",
"scrnCnt": "290",
"showCnt": "838"
},
{
"rnum": "6",
"rank": "6",
"rankInten": "1",
"rankOldAndNew": "OLD",
"movieCd": "19940256",
"movieNm": "라이온 킹",
"openDt": "1994-07-02",
"salesAmt": "507115500",
"salesShare": "6.6",
"salesInten": "-114593500",
"salesChange": "-18.4",
"salesAcc": "1841625000",
"audiCnt": "45750",
"audiInten": "-11699",
"audiChange": "-20.4",
"audiAcc": "171285",
"scrnCnt": "244",
"showCnt": "895"
},
{
"rnum": "7",
"rank": "7",
"rankInten": "-1",
"rankOldAndNew": "OLD",
"movieCd": "20113381",
"movieNm": "오싹한 연애",
"openDt": "2011-12-01",
"salesAmt": "344871000",
"salesShare": "4.5",
"salesInten": "-107005500",
"salesChange": "-23.7",
"salesAcc": "20634684500",
"audiCnt": "45062",
"audiInten": "-15926",
"audiChange": "-26.1",
"audiAcc": "2823060",
"scrnCnt": "243",
"showCnt": "839"
},
{
"rnum": "8",
"rank": "8",
"rankInten": "0",
"rankOldAndNew": "OLD",
"movieCd": "20112709",
"movieNm": "극장판 포켓몬스터 베스트 위시「비크티니와 백의 영웅 레시라무」",
"openDt": "2011-12-22",
"salesAmt": "167809500",
"salesShare": "2.2",
"salesInten": "-45900500",
"salesChange": "-21.5",
"salesAcc": "1897120000",
"audiCnt": "24202",
"audiInten": "-7756",
"audiChange": "-24.3",
"audiAcc": "285959",
"scrnCnt": "186",
"showCnt": "348"
},
{
"rnum": "9",
"rank": "9",
"rankInten": "0",
"rankOldAndNew": "OLD",
"movieCd": "20113311",
"movieNm": "앨빈과 슈퍼밴드3",
"openDt": "2011-12-15",
"salesAmt": "137030000",
"salesShare": "1.8",
"salesInten": "-35408000",
"salesChange": "-20.5",
"salesAcc": "3416675000",
"audiCnt": "19729",
"audiInten": "-6461",
"audiChange": "-24.7",
"audiAcc": "516289",
"scrnCnt": "169",
"showCnt": "359"
},
{
"rnum": "10",
"rank": "10",
"rankInten": "0",
"rankOldAndNew": "OLD",
"movieCd": "20112708",
"movieNm": "극장판 포켓몬스터 베스트 위시 「비크티니와 흑의 영웅 제크로무」",
"openDt": "2011-12-22",
"salesAmt": "125535500",
"salesShare": "1.6",
"salesInten": "-40756000",
"salesChange": "-24.5",
"salesAcc": "1595695000",
"audiCnt": "17817",
"audiInten": "-6554",
"audiChange": "-26.9",
"audiAcc": "235070",
"scrnCnt": "175",
"showCnt": "291"
}
]
}
}
package com.ssafy.day9.a_parse.dto;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class BoxOffice {
private Integer rank; // 등수
private String movieNm; // 영화제목
private Date openDt; // 개봉일
private Integer audiAcc;// 누적 관객 수
public Integer getRank() {
return rank;
}
public void setRank(Integer rank) {
this.rank = rank;
}
public String getMovieNm() {
return movieNm;
}
public void setMovieNm(String movieNm) {
this.movieNm = movieNm;
}
public Date getOpenDt() {
return openDt;
}
public void setOpenDt(Date openDt) {
this.openDt = openDt;
}
public Integer getAudiAcc() {
return audiAcc;
}
public void setAudiAcc(Integer audiAcc) {
this.audiAcc = audiAcc;
}
public Date toDate(String date) {
Date dateObj = null;
// TODO: 문자열 형태의 날짜를 Date로 변환해서 반환하시오.
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
dateObj = format.parse(date);
} catch (ParseException e) {
e.printStackTrace();
dateObj = new Date();
}
// END
return dateObj;
}
@Override
public String toString() {
return "[rank=" + rank + ", movieNm=" + movieNm + ", openDt=" + openDt + ", audiAcc=" + audiAcc + "]";
}
}
package com.ssafy.day9.a_parse.client;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import com.ssafy.day9.a_parse.dto.BoxOffice;
import com.ssafy.day9.a_parse.parser.BoxOfficeDomParser;
import com.ssafy.day9.a_parse.parser.BoxOfficeJsonParser;
import com.ssafy.day9.a_parse.parser.BoxOfficeParser;
import com.ssafy.day9.a_parse.parser.BoxOfficeSaxParser;
public class BoxOfficeCLI {
private BoxOfficeParser parser = null;
private InputStream resource = null;
public BoxOfficeCLI() {
}
private void readBoxOfficeList() {
// TODO: resource와 parser를 구성해서 정보를 가져와보자.
this.resource = BoxOfficeCLI.class.getResourceAsStream("../res/boxoffice.json");
// this.parser = BoxOfficeSaxParser.getParser();
// this.parser = BoxOfficeDomParser.getParser();
this.parser = BoxOfficeJsonParser.getParser();
// END
List<BoxOffice> list = parser.getBoxOffice(resource);
System.out.println("list size: " + list.size());
for (BoxOffice boxOffice : list) {
System.out.println(boxOffice);
}
}
public static void main(String[] args) {
BoxOfficeCLI cli = new BoxOfficeCLI();
cli.readBoxOfficeList();
}
}
package com.ssafy.day9.a_parse.parser;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ssafy.day9.a_parse.dto.BoxOffice;
public class BoxOfficeJsonParser implements BoxOfficeParser {
private static BoxOfficeJsonParser parser = new BoxOfficeJsonParser();
public static BoxOfficeJsonParser getParser() {
return parser;
}
private BoxOfficeJsonParser() {
System.out.println("json");
}
private List<BoxOffice> list;
@Override
public List<BoxOffice> getBoxOffice(InputStream resource) {
list = new ArrayList<>();
ObjectMapper mapper = new ObjectMapper();
// 날짜 변경과 관련된 룰 지정
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
// TODO: json을 파싱해서 list를 구성하시오.
try {
Map<String, Map<String, Object>> result = mapper.readValue(resource, Map.class);
List<Map<String, Object>> list = (List)result.get("boxOfficeResult").get("dailyBoxOfficeList");
for(Map<String, Object> info : list) {
BoxOffice office = mapper.convertValue(info, BoxOffice.class);
this.list.add(office);
}
} catch (JsonParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JsonMappingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// END
return list;
}
}
package com.ssafy.day9.a_parse.parser;
import java.io.InputStream;
import java.util.List;
import com.ssafy.day9.a_parse.dto.BoxOffice;
public interface BoxOfficeParser {
public abstract List<BoxOffice> getBoxOffice(InputStream resource);
}