Java EE 정리 20
26 Mar 2019
Reading time ~6 minutes
Java EE 정리 20 - XML, JDOM Parser, RSS Parsing
XML(eXtensible Markup Language)
- 데이터의 구조를 표현하는 태그 언어
- W3C에서 표준안을 제정, 발표
- 태그를 사용자가 정의하고 사용
- SGML에서 파생됨
- HTML과 다르게 XML은 DTD가 없음
- 태그글 사용자가 마음대로 정의가능
- 통일성이 없어 Parsing이 어렵다
- DTD 필요성이 대두됨
- HTML과 다르게 XML은 DTD가 없음
- SGML에서 파생됨
- 이기종 언어간의 데이터 전달을 목적으로 사용
- XML로 다른 언어끼리 교류가능
- C <-> Java
- Java <-> JavaScript
- Python <-> Java
- XML 태그(노드)사이 값을 가져오는 작업을 Parsing이라고 한다.
- XML로 다른 언어끼리 교류가능
<노드명>값</노드명>
- 프로그램에서 사용할 값을 저장
- 환결설정 목적 (Tomcat server.xml, web.xml 등)
- 국제화 목적 (똑같은 프로그램 여러 언어로 설정)
- 데이터 구조를 잘 표현하는 언어
- 사용자에게 보여지는 용도가 아님
- HTML과 다르게 Error 발생
- 시작 노드와 끝 노드는 반드시 쌍으로 존재해야 된다
- 대소문자 구분
- 최상위 부모 노드는 하나만 작성 가능
- Well-Formed Document(적격 문서)
- DTD없이 작성하는 XML
- 작성 규칙만 지켜서 만든 XML
- Valid Document(유효 문서)
- DTD를 정의하고 참조하여 작성하는 XML
- 작성 규칙과 DTD의 정의규칙을 지켜 만드는 XML
- 보통 DB의 레코드를 XML로 바꿔 사용
- 공공데이터의 결과를 XML로 많이 제공
- XML보다 JSON이 보편화 되고 있음
XML 작성법
- 선언부 - 무조건 첫줄에 나와야 한다
- standalone은 DTD여부 (yes/no)
<?xml version="1.0" encoding="UTF-8" standalone="yes|no"?>
- DTD가 존재하면
- SYSTEM은 DTD가 내 HDD에 있을 때
- PUBLIC은 DTD가 다른 웹 서버에 있을 때
<!DOCTYPE 적용노드명 SYSTEM "DTD의 위치">
<!DOCTYPE 적용노드명 PUBLIC "DTD의 URL">
- 부모노드는 XML에서 딱 1개 존재
- DOM tree - 태그를 트리형태로 그린 것
<부모노드>
<자식노드>
...
</자식노드>
</부모노드>
<depts>
<dept>
<dname>SI</dname>
<loc>서울</loc>
<deptno>10</deptno>
</dept>
<dept>
<dname>SM</dname>
<loc>대구</loc>
<deptno>20</deptno>
</dept>
<dept>
<dname>SE</dname>
<loc>부산</loc>
<deptno>30</deptno>
</dept>
</depts>
- XML 사용 예
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<msg>안녕 XML!</msg>
<msg>오늘은 화요일이야</msg>
</root>
- XML에서 에러가 발생하는 경우들
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- 선언부가 첫 라인에 없으면 에러 -->
<root>
<msg>안녕 XML!</msg>
<msg>오늘은 화요일이야</msg>
<!-- <msg>닫힘노드가 없으면 에러 -->
<!-- <Hello>대소문자 가린다</hello> -->
<msg/><!-- Element 사이에 값이 없다면/로 하나의 태그만 작성할 수 있다. -->
</root>
<!-- 최상위 부모노드는 반드시 하나만 존재해야 한다.
<root>
</root>
-->
- 사람의 정보를 저장하는 XML 만들기
- XML의 장점은 데이터의 정보를 명확히 알 수 있다.
<?xml version="1.0" encoding="UTF-8"?>
<people>
<person>
<name>
<firstName>영근</firstName>
<lastName>오</lastName>
</name>
<ssn>990133-*******</ssn>
<address>서울 강동구</address>
</person>
<person>
<name>
<firstName>민지</firstName>
<lastName>김</lastName>
</name>
<ssn>871112-*******</ssn>
<address>서울 서초구</address>
</person>
<person>
<name>
<firstName>소소</firstName>
<lastName>손</lastName>
</name>
<ssn>930812-*******</ssn>
<address>서울 관악구</address>
</person>
</people>
- XML을 Java로 Parser없이 Parsing하기
// 1. a.xml과 Stream 연결
BufferedReader br = new BufferedReader(new FileReader("xml의 경로"));
// 2. 줄단위로 끝까지 읽어들이기
String temp = "";
String data = "";
while((temp = br.readLine()) != null) {
// 3. 원하는 태그인지를 판단
if (temp.contains("<name>")) {
// 4. Parsing
data += temp.substring(temp.indexOf(">")+1, temp.lastIndexOf("<"));
}
}
<%!
public List<String> xmlParsing() throws IOException {
List<String> list = new ArrayList<String>();
BufferedReader br = null;
try {
// 1. XML과 연결
File file = new File("C:/.../xml0326/names.xml");
br = new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8"));
String temp = "";
String name = "";
// 2. 줄단위로 읽어들인다.
while((temp = br.readLine()) != null) {
if (temp.contains("<name>")) { // name노드가 이 있다면
name = temp.substring(temp.indexOf(">")+1, temp.lastIndexOf("<"));
list.add(name);
}
}
} finally {
if (br != null) { br.close(); }
}
return list;
}
%>
<table border="1">
<tr>
<th width="100">이름</th>
</tr>
<c:forEach var="name" items="<%= xmlParsing() %>">
<tr>
<td style="text-align: center;"><c:out value="${ name }"/></td>
</tr>
</c:forEach>
</table>
JDOM (XML Parser) 설치
- JDOM 사이트
- Downloads - Binaries - JDOM 2.0.6 다운로드
- jdom-2.0.6.jar를 WEB-INF/lib에 옮김
XML Parser
- XML을 Parsing할 수 있는 도구
- SAX(Simple API for XML) 방식 Parser
- 줄 단위로 XML을 분석하는 Parsing 도구
- sun사에서 제안
- DOM(Document Object Model) 방식 Parser
- Node를 객체로 만들어 작업
- IBM사에서 제안
- DOM방식이 사용상 편의성이 더 좋다
- JDOM은 두가지 방식을 혼합해서 만들어진 Parser
- JDOM Parser 사용법
// 1. builder 생성
SAXBuilder sb = new SAXBuilder();
// 2. XML을 문서 객체로 저장
// File이나 URL 등 어디서든 접근가능하도록 Overloading 되어있다.
Document doc = sb.build(new URL("http://.../ooo.xml "));
// xml자체가 doc이란 객체에 들어간 것
// 3. 최상위 부모 노드를 얻는다.
Element rootNode = doc.getRootElement();
// 4. 자식 노드들을 얻는다.
// List<Element> deptNodes = rootNode.getChildren(); // for문 사용가능
// 더 빠른 Iterator 사용 예
Iterator<Element> deptNodes = rootNode.getChildren().iterator();
// 5. 자식 노드들을 순환
Element someNode = null;
String nodeName = "";
String nodeVal = "";
while(deptNodes.hasNext()) {
// 6. 자식노드 얻기
someNode = deptNodes.next();
// 7. 노드명 구하기
nodeName = someNode.getName();
// 8. 노드값 구하기
nodeVal = someNode.getText();
}
// 이렇게 원하는 값 뽑아내서 사용
- JDOM Parser 사용 예
/**
* JDOM Parser 사용
*/
public class UseJDOMParser {
public UseJDOMParser() throws MalformedURLException, JDOMException, IOException {
// 1. Parser 생성
SAXBuilder sb = new SAXBuilder();
// 2. XML를 문서객체에 저장
Document doc = sb.build(new URL("http://localhost:8080/jsp_prj/xml0326/names.xml"));
// 3. 문서객체에서 최상위 부모 노드를 얻기
Element rootNode = doc.getRootElement();
// 4. 부모노드로부터 자식 노드들 얻기
Iterator<Element> nameNodes = rootNode.getChildren().iterator();
Element nameNode = null;
// 5. 자식노드 순환
while(nameNodes.hasNext()) {
// 6. 자식 노드 얻기
nameNode = nameNodes.next();
System.out.println("노드명 : " + nameNode.getName());
System.out.println("노드값 : " + nameNode.getText());
}
}
public static void main(String[] args) {
try {
new UseJDOMParser();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
RSS Parsing
- 데이터를 담을 VO 생성
public class RssVO {
private String title, link, description, pubDate;
// getter, setter, 기본생성자
...
- Parsing을 할 싱글톤패턴 적용한 클래스
public class JtbcRssParsing {
private static JtbcRssParsing jrp;
private JtbcRssParsing() {}
public static JtbcRssParsing getInstance() {
if (jrp == null) {
jrp = new JtbcRssParsing();
}
return jrp;
}
public List<RssVO> getNews(String rssName) throws IOException, JDOMException {
List<RssVO> list = new ArrayList<RssVO>();
StringBuilder xmlURL = new StringBuilder();
xmlURL.append("http://fs.jtbc.joins.com/RSS/");
if(rssName != null || "".equals(rssName)) {
xmlURL.append(rssName).append(".xml");
} else {
xmlURL.append("newsflash.xml");
}
// 1. Builder 생성
SAXBuilder sb = new SAXBuilder();
// 2. URL로 외부 서버에서 제공하는 RSS 문서 객체 얻기
Document doc = sb.build(new URL(xmlURL.toString()));
// 3. 최상위 부모노드 얻기
Element rootNode = doc.getRootElement(); // rss
// 4. 자식 노드(channel) 얻기
Element channelNode = rootNode.getChild("channel");
// 5. channel노드의 자식 노드들 얻기
Iterator<Element> someNodes = channelNode.getChildren().iterator();
Element someNode = null;
Iterator<Element> itemSubNodes = null;
Element itemSubNode = null;
RssVO rvo = null;
// channel 노드의 자식 노드들을 순환
while(someNodes.hasNext()) {
someNode = someNodes.next();
if("item".equals(someNode.getName())) {
itemSubNodes = someNode.getChildren().iterator();
rvo = new RssVO();
// item 노드의 자식 노드들을 순환
while(itemSubNodes.hasNext()) {
itemSubNode = itemSubNodes.next();
if ("title".equals(itemSubNode.getName())) {
rvo.setTitle(itemSubNode.getText());
}
if ("link".equals(itemSubNode.getName())) {
rvo.setLink(itemSubNode.getText());
}
if ("description".equals(itemSubNode.getName())) {
rvo.setDescription(itemSubNode.getText());
}
if ("pubDate".equals(itemSubNode.getName())) {
rvo.setPubDate(itemSubNode.getText());
}
}
// 순환 하며 RssVO에 setter로 값할당 후 list에 저장, 반복
list.add(rvo);
}
}
return list;
}
}
<!-- jtbc_rss.jsp -->
<style type="text/css">
...
td {
padding-left:3px;
}
</style>
...
<div id="subject" style="text-align:center;">
[ <a href="jtbc_rss.jsp?title=newsflash">속보</a> |
<a href="jtbc_rss.jsp?title=politics">정치</a> |
<a href="jtbc_rss.jsp?title=economy">경제</a> |
<a href="jtbc_rss.jsp?title=society">사회</a> |
<a href="jtbc_rss.jsp?title=international">국제</a> |
<a href="jtbc_rss.jsp?title=culture">문화</a> |
<a href="jtbc_rss.jsp?title=entertainment">연예</a> |
<a href="jtbc_rss.jsp?title=sports">스포츠</a> |
<a href="jtbc_rss.jsp?title=fullvideo">풀영상</a> |
<a href="jtbc_rss.jsp?title=newsrank">뉴스랭킹</a> |
<a href="jtbc_rss.jsp?title=newsroom">뉴스룸</a> |
<a href="jtbc_rss.jsp?title=morningand">아침&</a> |
<a href="jtbc_rss.jsp?title=newssite">뉴스현장</a> |
<a href="jtbc_rss.jsp?title=politicaldesk">정치부회의</a> ]
</div>
<div id="newsView">
<%
String rssName = request.getParameter("title");
if (rssName == null) {
rssName = "newsflash";
}
List<RssVO> list = JtbcRssParsing.getInstance().getNews(rssName);
pageContext.setAttribute("newsList", list);
%>
<c:if test="${ empty newsList }">
네트워크가 연결이 불안정합니다. 잠시 후 다시 시도해주세요.
</c:if>
<c:forEach var="news" items="${ newsList }">
<table style="border:1px solid #333; border-spacing:0px; margin:0px auto; margin-top:6px; ">
<tr>
<td style="width:50px; text-align:center; border-right:1px solid #333;">제목</td>
<td colspan="3" style="width:650px; text-align:center">
<c:out value="${ news.title }"/>
</td>
</tr>
<tr>
<td style="width:50px; text-align:center; border-right:1px solid #333;">링크</td>
<td><a href="${ news.link }" target="_new">뉴스이동</a></td>
<td style="width:50px; text-align:left;">작성일</td>
<td><c:out value="${ news.pubDate }"/></td>
</tr>
<tr>
<td style="width:50px; text-align:center; border-right:1px solid #333;">내용</td>
<td colspan="3" style="width:650px;">
<c:out value="${ news.description }"/>
</td>
</tr>
</table>
</c:forEach>
</div>