공공데이터와 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 노드(주소록)을 시작으로 부모-자식의 관계 구성
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;
}