Web/JAVA

XML 파싱

당진개발자 2024. 1. 25. 10:57

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);
}