공공데이터와 XML
공공데이터 소개
공공데이터란?
- 공공기관이 만들어내는 모든 공적인 정보
- 각 공공기관이 보유한 데이터를 개방하여 누구나 원하는 기능에 활용 가능
- www.data.go.kr 등
데이터의 형태
- CSV : comma separated value
- 용량이 작지만 구조적이지 못함
- xml : 태그를 통해 데이터 형식 정의
- 구조적, 정확한 문법이 필요, 큰 용량
- json : JavaScript Object Notation 을 통해 데이터 형식 정의
- 구조를 가지며 객체로 다른 언어와 호환
XML
- Markup Language
- 태그 등을 이용하여 문서나 데이터의 구조를 명기하는 언어
- HTML, SGML, …
- XML
- Extensible Markup Language
- HTML과 달리
- 필요에 따라 태그를 확장에서 사용 가능
- 정확한 문법을 지켜야 동작: Well formed
기본 문법
- 문서의 시작은 <?xml version=”1.0” encoding=”UTF-8”?>로 한다.
- 반드시 root element가 존재해야 한다.
- 나머지 태그들은 tree 형태로 구성
- 시작 태그와 종료 태그는 일치해야 한다.
- 시작 태그는 key-value 구조의 속성을 가질 수 있다.
- 속성값은 “ “ 또는 ‘ ‘로 묶어서 표현한다.
- 태그는 대소문자를 구별한다.
valid
- xml 태그는 자유롭게 생성하기 때문에 최초 작성자의 의도대로 작성되는지 확인할 필요
- 문서의 구조와 적절한 요소, 속성들의 개수, 순서들이 잘 지켜졌는가?
- DTD 또는 Schema를 이용해서 문서의 규칙 작성; 이를 잘 따른 문서를 valid하다 라고 함
문서의 Parsing
XML 파싱
파싱
- 문서에서 필요한 정보를 얻기 위해 태그를 구별하고 내용을 추출하는 과정
- 전문적인 parser 활용
- SAX parser
- Simple API for XML parser
- 문서를 읽으면서 태그의 시작, 종료 등 이벤트 기반으로 처리하는 방식
- DOM parser
- Document Object Model
- 문서를 다 읽고 난 후 문서 구조 전체를 자료구조에 저장하여 탐색하는 방식
- SAX는 빠르고 한번에 처리하기 때문에 다양한 탐색이 어렵다.
- DOM은 다양한 탐색이 가능하지만 느리고 무거우며 큰 문서를 처리하기 어렵다.
SAX Parser
동작 방식
- 문서를 읽다가 발생하는 이벤트 기반으로 문서 처리
- DefaultHandler를 사용 -> extends DefaultHandler 필수!!!
문자열 형태의 날짜를 Date로 변환해서 반환
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
return format.parse(date);
} catch(ParseException e) {
e.printStackTrace();
return new Date();
}
Handler 작성
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.parse(xml, this);
} catch (IOException | SAXException | ParserConfigurationException e) {
e.printStackTrace();
}
@Override
public void startDocument() throws SAXException {
System.out.println("문서 시작");
}
@Override
public void endDocument() throws SAXException {
System.out.println("문서 종료");
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if(qName.equals("dailyBoxOffice")) {
current = new BoxOffice();
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
this.content = new String(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if(qName.equals("dailyBoxOffice")) {
list.add(current);
current = null;
} else if (qName.equals("rank")) {
current.setRank(Integer.parseInt(content));
} else if (qName.equals("movieNm")) {
current.setMovieNm(content);
} else if(qName.equals("openDt")) {
current.setOpenDt(current.toDate(content));
} else if(qName.equals("audiAcc")) {
current.setAudiAcc(Integer.parseInt(content));
}
}
DOM Parser
동작 방식
- 문서를 완전히 메모리에 로딩 후 필요한 내용 찾기
- DOM Tree
- 문서를 구성하는 모든 요소를 Node(태그, 속성, 값)로 구성
- root Node, element Node, attribute Node, text Node
- 태그들은 root 노드(주소록)을 시작으로 부모-자식의 관계 구성
- 문서를 구성하는 모든 요소를 Node(태그, 속성, 값)로 구성
public List<BoxOffice> getBoxOffice() {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 문서 로딩 완료 --> 원하는 요소들 골라내기
Document doc = builder.parse(xml);
// 최 상위 element
Element root = doc.getDocumentElement();
parse(root);
} catch (IOException | ParserConfigurationException | SAXException e) {
e.printStackTrace();
}
return list;
}
private void parse(Element root) {
NodeList boxOffices = root.getElementsByTagName("dailyBoxOffice");
for(int i=0; i<boxOffices.getLength(); i++) {
Node child = boxOffices.item(i);
list.add(getBoxOffice(child));
}
}
private static BoxOffice getBoxOffice(Node node) {
BoxOffice boxOffice = new BoxOffice();
// TODO: node 정보를 이용해서 BoxOffice를 구성하고 반환하시오.
NodeList subNodes = node.getChildNodes();
for(int j=0; j<subNodes.getLength(); j++) {
Node sub = subNodes.item(j);
if(sub.getNodeName().equals("rank")) {
boxOffice.setRank(Integer.parseInt(sub.getTextContent()));
} else if(sub.getNodeName().equals("movieNm")) {
boxOffice.setMovieNm(sub.getTextContent());
} else if(sub.getNodeName().equals("openDt")) {
boxOffice.setOpenDt(boxOffice.toDate(sub.getTextContent()));
} else if(sub.getNodeName().equals("audiAcc")) {
boxOffice.setAudiAcc(Integer.parseInt(sub.getTextContent()));
}
}
// END:
return boxOffice;
}
JSON
- JavaScript Object Notation(자바스크립트에서의 객체 표현법)
- 간결한 문법, 단순한 텍스트, 적은 용량으로 대부분의 언어, 대부분의 플랫폼에서 사용 가능
- 기종간의 데이터 교환에 광범위하게 사용됨
- 객체를 key-value의 쌍으로 관리
- 다양한 라이브러리를 이용한 간편한 사용
public List<BoxOffice> getBoxOffice() {
ObjectMapper mapper = new ObjectMapper();
// 날짜 변경과 관련된 룰 지정
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
// 기본적으로 map의 구조를 가짐
Map<String, Map<String, Object>> result = mapper.readValue(json, Map.class);
// 배열은 List
List<Map<String, Object>> list = (List)result.get("boxOfficeResult").get("dailyBoxOfficeList");
for(Map<String, Object> info : list) {
BoxOffice boxOffice = mapper.convertValue(info, BoxOffice.class);
System.out.println(boxOffice);
}
return list;
}