2012년 1월 14일 토요일

XML 파싱하기

참고 - http://www.w3.org/TR/REC-xml/
(Extensible Markup Language (XML) 1.0 (Fifth Edition))


XML 파서의 종류는 SAX 파서와 DOM 파서가 있다.

SAX 파서와 DOM 파서의 차이점:

참고 - http://www.ibiblio.org/xml/books/xmljava/chapters/ch09s11.html
(Choosing between SAX and DOM)

 > 요약: 어떤 파서를 사용할지는 아래와 같은 상황을 고려하여 선택하는 편이 좋다.

SAX 파서를 사용하는 상황
  • 파싱하려는 XML 크기가 너무 클 때
  • 파싱하려는 XML 중 필요한 정보만 취합하고자 할 때
  • 실시간으로 파싱을 처리하고자 할때

DOM 파서를 사용하는 상황
  • XML 문서안에 넓게 분포된 자료들 한번에 취합하고자 할 때
  • 파싱하려는 XML 문서 구조가 매우 복잡할 때
  • XML 문서를 수정할 때
  • 파싱한 XML 정보를 여러 함수들이 사용하고자 할 때

파서 사용법:

예) RSS 파일을 파싱하는 예

NY times 사이트의 RSS 링크 중 하나를 선택하여 nyfeed.xml 이라는 파일명으로 저장하였다.
RSS 항목들 중 item 항목에 대한 title 과 link 만 추려서 출력한다.


프로젝트 구조는 아래와 같다.

실행 결과는 아래와 같다.

DOM 파서

SAX 파서

nyfeed.xml
아래 링크로 접속하여 소스 보기 한 후 내용을 복사하여 프로젝트 폴더에 nyfeed.xml 파일로 저장
http://feeds.nytimes.com/nyt/rss/World

XMLParseTest.java
같은 일을 하는 함수 두개를 만들었다. domParseTest() 와 saxParseTest() 함수이다.

1. nyfeed.xml 파일을 읽어 파싱한다.
2. item 항목의 title 과 link 내용을 출력한다.

package xml.parse;

import java.io.File;

import javax.xml.parsers.*;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * XMLParserTest
 */
public class XMLParserTest {
 
 private final String XML_FILE_PATH = "nyfeed.xml";

 /**
  * 
  * @param args
  * @throws Exception
  */
 public static void main(String[] args) throws Exception {
  XMLParserTest xpt = new XMLParserTest();
  xpt.domParseTest();
  xpt.saxParseTest();
 }
 
 /**
  * 
  * @throws Exception
  */
 public void domParseTest() throws Exception {

  System.out.println("==============================");
  System.out.println("domParseTest()");
  System.out.println("==============================");
  
  File xmlFile = new File(XML_FILE_PATH);
  
  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  DocumentBuilder db = dbf.newDocumentBuilder();
  Document doc = db.parse(xmlFile);
  
  doc.getDocumentElement().normalize();
  
  System.out.printf("Root element:%s\n", doc.getDocumentElement().getNodeName());
  NodeList itemNodeList = doc.getElementsByTagName("item");
  
  for (int s = 0; s < itemNodeList.getLength(); s++) {

   Node itemNode = itemNodeList.item(s);

   if (itemNode.getNodeType() == Node.ELEMENT_NODE) {

    Element itemElement = (Element)itemNode;
    
    NodeList titleNodeList = itemElement.getElementsByTagName("title");
    Element titleElement = (Element)titleNodeList.item(0);
    NodeList childTitleNodeList = titleElement.getChildNodes();
    System.out.printf("[title : %s]\n", ((Node)childTitleNodeList.item(0)).getNodeValue());
    
    NodeList linkNodeList = itemElement.getElementsByTagName("link");
    Element linkElement = (Element) linkNodeList.item(0);
    NodeList childLinkNodeList = linkElement.getChildNodes();
    System.out.printf("[link : %s]\n", ((Node)childLinkNodeList.item(0)).getNodeValue());
   }

  }
 }
 
 /**
  * 
  * @throws Exception
  */
 public void saxParseTest() throws Exception {
  
  System.out.println("==============================");
  System.out.println("saxParseTest()");
  System.out.println("==============================");
  
  File xmlFile = new File(XML_FILE_PATH);
  
  SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
  DefaultHandler dh = new DefaultHandler() {
   
   private boolean firstElement = true;
   private boolean inItem = false;
   private boolean inTitle = false;
   private boolean inLink = false;
   private StringBuilder characterSB;
   
   @Override
   public void startDocument() throws SAXException {
    System.out.println("startDocument");
    super.startDocument();
   }

   @Override
   public void endDocument() throws SAXException {
    System.out.println("endDocument");
    super.endDocument();
   }

   @Override
   public void startElement(String uri, String localName,
     String qName, Attributes attributes) throws SAXException {
    
    if (firstElement) {
     System.out.printf("Root element:%s\n", qName);
     firstElement = false;
    }
    
    if (qName.equals("item")) {
     inItem = true;
    } else if (qName.equals("title")) {
     inTitle = true;
    } else if (qName.equals("link")) {
     inLink = true;
    }
    
    if (inItem && (inTitle || inLink)) {
     characterSB = new StringBuilder();
    }
    
    super.startElement(uri, localName, qName, attributes);
   }

   @Override
   public void characters(char[] ch, int start, int length)
     throws SAXException {
    
    if (characterSB != null) {
     characterSB.append(handleCharacters(ch, start, length));
    }
    
    super.characters(ch, start, length);
   }

   @Override
   public void endElement(String uri, String localName, String qName)
     throws SAXException {
    
    if (characterSB != null) {
     if (inItem && inTitle) {
      System.out.printf("[title : %s]\n", characterSB.toString());
     } else if (inItem && inLink) {
      System.out.printf("[link : %s]\n", characterSB.toString());
     }
     characterSB = null;
    }
    
    if (qName.equals("item")) {
     inItem = false;
    } else if (qName.equals("title")) {
     inTitle = false;
    } else if (qName.equals("link")) {
     inLink = false;
    }
    
    super.endElement(uri, localName, qName);
   }
   

   /**
    * 
    * @param ch
    * @param start
    * @param end
    * @return
    */
   public String handleCharacters(char[] ch, int start, int length) {
    
    StringBuilder sb = new StringBuilder();
    for (int i = start; i < start + length; i++) {
     sb.append(ch[i]);
    }
    return sb.toString();
   }
  };
  parser.parse(xmlFile, dh);
 }
}


팁)
SAX 파싱 작업 중 더 이상 작업 할 필요가 없을 때는 SAXException 을 날려서 중단하면 된다.
참고 - http://www.ibm.com/developerworks/xml/library/x-tipsaxstop/
(Tip: Stop a SAX parser when you have enough data)

throw new SAXException("Enough!");

댓글 없음:

댓글 쓰기