3. XML Parser - rssParser.js
 
먼저의 post에서 var xml = rssParser(req.responseXML); 를 보셨을 것입니다.
req.responseXML 을 받아서 JSON으로 파싱 하는 것입니다.
이부분은 rssParser.js란 javascript파일로 따로 구현해 보았습니다.
 
 
먼저 전체 소스를 보시죠.
 
  0: /**
  1:  *  @(#)rssParser.js    V0.1    2007/03/15
  2:  *
  3:  *  rss XML Parser extend Prototype.js v1.5
  4:  *  Copyright 2005-2007 by VRICKS, All Right Reserved.
  5:  *  http://www.vricks.com
  6:  *
  7:  *  GNU LESSER GENERAL PUBLIC LICENSE
  8:  *  TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
  9:  *
 10:  *  @Author Woo-Chang Yang, routine@vrick.com
 11:  */
 12: 
 13: function rssParser(xml) {
 14:     var v = Try.these(
 15:         // Rss ¹öÀüÀ» °¡Á® ¿É´Ï´Ù.
 16:         function() {
 17:             return xml.getElementsByTagName("rss")[0].getAttribute("version") ? "2.0" : false;
 18:         },
 19:         function() {
 20:             return xml.getElementsByTagName("rdf:RDF")[0].getAttribute("xmlns") ? "1.0" : false;
 21:         },
 22:         function() {
 23:             return xml.getElementsByTagName("feed")[0].getAttribute("xmlns") ? "atom" : false;
 24:         }
 25:     )
 26:     switch(v) {
 27:         case "2.0"  : return new rssParser2(xml); break;
 28:         case "1.0"  : return new rssParser1(xml); break;
 29:         case "atom" : return new rssParserAtom(xml); break;
 30:         default     : return false;
 31:     }
 32: };
 33: 
 34: // Rss 2.0 Calss
 35: var rssParser2 = Class.create();
 36: Object.extend(rssParser2.prototype, {
 37:     initialize    : function(xml) {
 38:         var channel = xml.getElementsByTagName("channel")[0];
 39:         this.title  = channel.getElementsByTagName("title")[0].firstChild.nodeValue;
 40:         this.link   = channel.getElementsByTagName("link")[0].firstChild.nodeValue;
 41:         if(channel.getElementsByTagName("image")[0]) {
 42:             var images   = channel.getElementsByTagName("image")[0];
 43:             this.image   = {
 44:                 "url"    : images.getElementsByTagName("url")[0].firstChild.nodeValue,
 45:                 "title"  : images.getElementsByTagName("title")[0].firstChild.nodeValue,
 46:                 "link"   : images.getElementsByTagName("link")[0].firstChild.nodeValue
 47:             };
 48:         }
 49:         else {
 50:             this.image = {
 51:                 "url"    : "",
 52:                 "title"  : "",
 53:                 "link"   : ""
 54:             }
 55:         }
 56:         this.description = Try.these (
 57:             function() {
 58:                 return channel.getElementsByTagName("description")[0].firstChild.nodeValue;
 59:             },
 60:             function() { return "" }
 61:         );
 62:         this.language    = Try.these (
 63:             function() {
 64:                 return channel.getElementsByTagName("language")[0].firstChild.nodeValue;
 65:             },
 66:             function() {
 67:                 return channel.getElementsByTagName("dc:language")[0].firstChild.nodeValue;
 68:             },
 69:             function() { return ""}
 70:         );
 71:         this.pubDate     = Try.these(
 72:             function() {
 73:                 return channel.getElementsByTagName("pubDate")[0].firstChild.nodeValue;
 74:             },
 75:             function() {
 76:                 return channel.getElementsByTagName("lastBuildDate")[0].firstChild.nodeValue;
 77:             },
 78:             function() { return ""; }
 79:         );
 80:         var items = new Array();
 81:         $A(channel.getElementsByTagName("item")).each(function(i){
 82:             items.push({
 83:                 "category" : Try.these(
 84:                     function() {
 85:                         return i.getElementsByTagName("category")[0].firstChild.nodeValue;
 86:                     },
 87:                     function() { return "" }
 88:                 ),
 89:                 "title" : i.getElementsByTagName("title")[0].firstChild.nodeValue,
 90:                 "link"    : i.getElementsByTagName("link")[0].firstChild.nodeValue,
 91:                 "description" : Try.these (
 92:                     function() {
 93:                         return i.getElementsByTagName("description")[0].firstChild.nodeValue;
 94:                     },
 95:                     function() { return "" }
 96:                 ),
 97:                 "pubDate" : Try.these (
 98:                     function() {
 99:                         return i.getElementsByTagName("pubDate")[0].firstChild.nodeValue;
100:                     },
101:                     function() {
102:                         return i.getElementsByTagName("dc:date")[0].firstChild.nodeValue;
103:                     },
104:                     function() { return "" }
105:                 )
106:             })
107:         })
108:         this.item = items;
109:     }
110: });
111: 
112: var rssParser1 = Class.create();
113: Object.extend(rssParser1.prototype, {
114:     initialize    : function(xml) {
115:         var channel = xml.getElementsByTagName("channel")[0];
116:         var images  = xml.getElementsByTagName("image")[0];
117:         this.title  = channel.getElementsByTagName("title")[0].firstChild.nodeValue;
118:         this.link   = channel.getElementsByTagName("link")[0].firstChild.nodeValue;
119:         this.image  = {
120:             "url"   : images.getElementsByTagName("url")[0].firstChild.nodeValue,
121:             "title" : images.getElementsByTagName("title")[0].firstChild.nodeValue,
122:             "link"  : images.getElementsByTagName("link")[0].firstChild.nodeValue
123:         };
124:         this.description = channel.getElementsByTagName("description")[0].firstChild.nodeValue;
125:         this.language    = channel.getElementsByTagName("dc:language")[0].firstChild.nodeValue;
126:         this.pubDate     = channel.getElementsByTagName("dc:date")[0].firstChild.nodeValue;
127:         var items        = xml.getElementsByTagName("item");
128:         var itemValue    = new Array();
129:         for(var i = 0; i < items.length; i++) {
130:             itemValue.push({
131:                 "category"   : items[i].getElementsByTagName("category")[0].firstChild.nodeValue,
132:                 "title"      : items[i].getElementsByTagName("title")[0].firstChild.nodeValue,
133:                 "link"       : items[i].getElementsByTagName("link")[0].firstChild.nodeValue,
134:                 "description":
135:                               items[i].getElementsByTagName("description")[0].firstChild.nodeValue,
136:                 "pubDate"    : items[i].getElementsByTagName("dc:date")[0].firstChild.nodeValue
137:             });
138:         };
139:         this.item = itemValue;
140:     }
141: });
142: 
143: var rssParserAtom = Class.create();
144: Object.extend(rssParserAtom.prototype, {
145:     initialize    : function(xml) {
146:         this.title   = xml.getElementsByTagName("title")[0].firstChild.nodeValue;
147:         this.link    = xml.getElementsByTagName("link")[0].getAttribute("href");
148:         this.image   = {
149:             "url"    : "",
150:             "title"  : "",
151:             "link"   : ""
152:         };
153:         this.description = xml.getElementsByTagName("info")[0].firstChild.nodeValue;
154:         this.language    = "";
155:         this.pubDate     = xml.getElementsByTagName("modified")[0].firstChild.nodeValue;
156:         var items        = xml.getElementsByTagName("entry");
157:         var itemValue    = new Array();
158:         for(var i = 0; i < items.length; i++) {
159:             itemValue.push({
160:                 "category"   : "",
161:                 "title"      : items[i].getElementsByTagName("title")[0].firstChild.nodeValue,
162:                 "link"       : items[i].getElementsByTagName("link")[0].getAttribute("href"),
163:                 "description":items[i].getElementsByTagName("summary")[0].firstChild.nodeValue,
164:                 "pubDate"    : items[i].getElementsByTagName("created")[0].firstChild.nodeValue
165:             });
166:         };
167:         this.item = itemValue;
168:     }
169: });
 
function rssParser(xml) {...}을 보시면 Rss의 버전을 구해서 알맞은 Class를 호출 하는 부분 입니다.
Try.these (...) 부분이 보이실 것입니다. API 보기
아래에 나열된 function()을 수행하여 먼저 성공한 하나만을 호출 합니다. 정말 멋진 생각입니다. ^^; 
var rssParser2 = Class.create();
Object.extend(rssParser2.prototype, { ... }
새로운 Class를 생성하여 prototype을 확장 하였습니다.
Class.create()로 확장을 하면 항상 initialize 를 수행 합니다.
new rssParser2(xml); 하게 되면 자동으로 initialize 부분이 수행이 된다는 의미 입니다.
var channel = xml.getElementsByTagName("channel")[0];
태그명 channel의 첫번째 element를 가져 옵니다. channel이 한개뿐인데 배열로 가져 왔습니다.
태그명으로 가져 올 때는 getElementsByTagName 여기서도 보실수 있듯이 복수형입니다.
그래서 항상 배열형태로 리턴이 됩니다. [0] 이분이 빠지면 에러가 납니다. ^^;
이제 channel의 자식 노드들을 뽑아올 차례 입니다.
this.title  = channel.getElementsByTagName("title")[0].firstChild.nodeValue;
<channel>아래에 있는 태그명 <title>의 첫번째 노드의 value를 가져 옵니다.
DOM에 대한 자세한 내용은
URL : http://www.ibm.com/developerworks/kr/library/wa-ajaxintro5/ 을 참고 하시기 바랍니다. 
이렇게 title, lilnk, descriiption을 가져옵니다.
pubDate의 경우는 먼저 pubDate 태그를 찾은 후 없으면 lastBuildDate를 그것도 없으면 공백문자를 리턴해 줍니다.
뭐 어려운거 없습니다. 그냥 DOM으로 노드 뽑아 오듯이 하나씩 뽑아서 대입해 주면 됩니다.
<item>태그에 들어있는 각각의 post를 가져올 차례입니다.
$A(channel.getElementsByTagName("item")).each(function(i){ ... })
$A는 Array의 확장판으로 Ruby틱한 배열형태를 사용할 수 있도록 해 줍니다. API 보기 
<item>을 돌면서 <item>에 포함한 <title>, <link>, <description>, <pubDate>를 가져와서 JSON형태로 파싱하고
파싱된 것들을 item이란 변수에 배열로 담아 놓습니다.
이렇게 해 놓으면 후에 item[i].title, item[i].link, item[i].description 으로 가져올 수 있습니다.
어떻게 글로 설명을 할려니 더 어려워 진거 같네요.
구현된 모습을 한번 보겠습니다. 

이것으로 이번 post는 마치겠습니다.
시간이 되면 rssParser.js를 이용한 구글 개인화 페이지를 구현해 보도록 하겠습니다.
물론 prototype.js를 이용할 것이며 Scriptaculous 의 이펙트도 이용할 것입니다.
화면상으로 잠시 맛배기를 ^^;




0