Mac De PL/SQL RSS Reader #27 Tweet
RSS Readerの続き。
週末、美味しいワインのつまみのレシピを多摩美の芸術祭りに模擬店を出しているmiwaさんに教えてもらいニッコリ!!。
さて、APEX2.2に組み込んだPL/SQLで作成したRSS Readerストアドプロシージャのソースを公開。
ひとまず、これで、RSS Readerネタは終了。
Oracle Portalのデータベースポートレット向けにするとか、XSLT版というのも面白いと思っているが、それらはまた、気が向いたときにでも。。
以下は、APEXに組み込んだデモをQiuckTime Movieにしたもの。(再生にはQuickTimeが必要です。)

そして、RSS Readerストアドプロシージャのソースは以下。
ただし、ブラウザに表示するためソースコードの一部の文字を
文字参照に置換してある。( < > " ¥ など)
オリジナルのスクリプトrss_reader.sqlはこちら。(エンコーディングは、shift_jis。)
(RFC822や、RFC3339のDate / Time関連関数は、ストアドプロシージャの内部関数化してある。これらをパッケージ化するほうが分かりやすくなるかもしれないが、それらの内部関数を単独で利用することもないのでストアドプロシージャの内部関数としてある。)
CREATE OR REPLACE PROCEDURE RSS_READER
(
i_feedXmlUrl IN VARCHAR2
)
AS
--============ TYPEs/VARIABLEs ===============================================
C_RSS CONSTANT VARCHAR2(7) := 'rss';
C_RDF CONSTANT VARCHAR2(7) := 'rdf:RDF';
C_ATOM CONSTANT VARCHAR2(7) := 'feed';
-- C_ATTR_NAME_VERSION CONSTANT VARCHAR2(7) := 'version';
v_feedXmlUrl VARCHAR2(32767);
TYPE FeedEntryType IS RECORD
(
title VARCHAR2(2000),
link VARCHAR2(200),
published TIMESTAMP WITH LOCAL TIME ZONE
);
v_feedEntryList DBMS_XMLDOM.DomNodeList;
v_feedTitle VARCHAR2(2000);
v_feedUrl VARCHAR2(200);
v_myParser DBMS_XMLPARSER.Parser;
v_feedDoc DBMS_XMLDOM.DomDocument;
v_rootElementTagName VARCHAR2(7);
-- v_feedVersion VARCHAR2(40);
v_feedSourceClob CLOB;
v_nsAttrXmlNameSpaces VARCHAR2(32767);
--========== Internal PROCEDUREs/FUNCTIONs ===================================
FUNCTION RFC3339_DateStrToTimeStampLTZ(
i_dateTimeString IN VARCHAR2
)
RETURN TIMESTAMP WITH LOCAL TIME ZONE
AS
C_NLS_DATE_LANGUAGE CONSTANT VARCHAR2(40) := 'NLS_DATE_LANGUAGE=AMERICAN';
v_tempTimeStampTz TIMESTAMP WITH LOCAL TIME ZONE := NULL;
v_dateTimeString VARCHAR2(50) := NULL;
v_timeStampTzFormat VARCHAR2(40) := NULL;
BEGIN
v_dateTimeString := REGEXP_REPLACE(
REGEXP_REPLACE(
UPPER(i_dateTimeString),
'T', NULL, 1, 1
),
'Z$', 'UTC'
);
IF v_dateTimeString IS NULL THEN
RETURN NULL;
END IF;
v_timeStampTzFormat := 'RRRR-MM-DD';
IF LENGTH(v_dateTimeString) >= 10 THEN
IF REGEXP_INSTR(v_dateTimeString , '([0-9]{2}:){2}[0-9]{2}¥.?') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'HH24:MI:SSXFF';
END IF;
IF REGEXP_INSTR(v_dateTimeString, '(¥+|¥-)[0-9]{2}:[0-9]{2}$') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'TZH:TZM';
ELSIF REGEXP_INSTR(v_dateTimeString, 'UTC$') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'TZR';
END IF;
END IF;
v_tempTimeStampTz := TO_TIMESTAMP_TZ(
v_dateTimeString,
v_timeStampTzFormat,
c_nls_date_language
);
RETURN v_tempTimeStampTz;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(
-20002,
'RFC3339_DateStrToTimeStampLTZ():' || SQLERRM()
);
RETURN NULL;
END RFC3339_DateStrToTimeStampLTZ;
FUNCTION RFC822_DateStrToTimeStampLTZ
(
i_dateTimeString IN VARCHAR2
)
RETURN TIMESTAMP WITH LOCAL TIME ZONE
AS
C_NLS_DATE_LANGUAGE CONSTANT VARCHAR2(40) := 'NLS_DATE_LANGUAGE=AMERICAN';
v_tempTimeStampTz TIMESTAMP WITH LOCAL TIME ZONE := NULL;
v_dateTimeString VARCHAR2(50) := NULL;
v_timeStampTzFormat VARCHAR2(40) := NULL;
BEGIN
v_dateTimeString := UPPER(i_dateTimeString);
IF v_dateTimeString IS NULL THEN
RETURN NULL;
END IF;
IF REGEXP_INSTR(v_dateTimeString, '^[A-Z]{3}¥, ') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'DY, ';
END IF;
v_timeStampTzFormat := v_timeStampTzFormat || 'DD MON RRRR';
IF REGEXP_INSTR(v_dateTimeString, '[0-9]{2}:[0-9]{2}:[0-9]{2}') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'HH24:MI:SS ';
ELSIF REGEXP_INSTR(v_dateTimeString, '[0-9]{2}:[0-9]{2}') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'HH24:MI ';
END IF;
IF REGEXP_INSTR(v_dateTimeString, ' (¥+|¥-)[0-9]{4}$') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'TZHTZM';
ELSIF REGEXP_INSTR(v_dateTimeString, ':[0-9]{2}$') = 0
AND REGEXP_INSTR(v_dateTImeString, '[0-9]{2} [A-Z]{3} [0-9]{2,4}$') = 0
AND REGEXP_INSTR(v_dateTImeString, ' [A-Z]{1,3}$') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'TZR';
END IF;
v_tempTimeStampTz := TO_TIMESTAMP_TZ(
v_dateTimeString,
v_timeStampTzFormat,
c_nls_date_language
);
RETURN v_tempTimeStampTz;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(
-20001,
'RFC822_DateStrToTimeStampLTZ():' || SQLERRM()
);
RETURN NULL;
END RFC822_DateStrToTimeStampLTZ;
PROCEDURE getFeedSource(
i_feedXmlUrl IN VARCHAR2,
i_feedXmlCharset IN VARCHAR2 DEFAULT 'UTF-8',
io_feedSourceClob IN OUT NOCOPY CLOB
)
IS
v_httpReq UTL_HTTP.REQ;
v_httpResp UTL_HTTP.RESP;
v_feedSource VARCHAR2(1024);
BEGIN
v_httpReq := UTL_HTTP.BEGIN_REQUEST(i_feedXmlUrl);
UTL_HTTP.SET_HEADER(
v_httpReq,
'User-Agent',
'Oracle UTL_HTTP/Oracle10g R1;'
);
UTL_HTTP.SET_HEADER(
v_httpReq,
'Content-Type',
'text/xml;charset='||i_feedXmlCharset
);
v_httpResp := UTL_HTTP.GET_RESPONSE(v_httpReq);
DBMS_LOB.CREATETEMPORARY(io_feedSourceClob, FALSE);
BEGIN
LOOP
UTL_HTTP.READ_LINE(v_httpResp, v_feedSource, true);
v_feedSource := v_feedSource || UTL_TCP.CRLF;
DBMS_LOB.WRITEAPPEND(
io_feedSourceClob,
LENGTH(v_feedSource),
v_feedSource
);
END LOOP;
EXCEPTION
WHEN UTL_HTTP.END_OF_BODY THEN
UTL_HTTP.END_RESPONSE(v_httpResp);
END;
END getFeedSource;
PROCEDURE getNameSpaces(
i_feedType IN VARCHAR2,
i_feedDoc IN DBMS_XMLDOM.DomDocument,
io_feedXmlNameSpaces IN OUT NOCOPY VARCHAR2
)
IS
v_attrTagName VARCHAR2(32767);
v_feedNodelist DBMS_XMLDOM.DomNodeList;
v_nsAttrNamedNodeMap DBMS_XMLDOM.DomNamedNodeMap;
BEGIN
CASE i_feedType
WHEN C_RSS THEN
v_feedNodeList := DBMS_XMLDOM.GETELEMENTSBYTAGNAME(i_feedDoc, C_RSS);
WHEN C_RDF THEN
-- name spaceが影響しているのだと思われるが上手い解決方法が
-- 見つからなかったので、全要素を取得している。
v_feedNodeList := DBMS_XMLDOM.GETELEMENTSBYTAGNAME(i_feedDoc, '*');
WHEN C_ATOM THEN
v_feedNodeList := DBMS_XMLDOM.GETELEMENTSBYTAGNAME(i_feedDoc, C_ATOM);
END CASE;
v_nsAttrNamedNodeMap
:= DBMS_XMLDOM.GETATTRIBUTES(DBMS_XMLDOM.ITEM(v_feedNodeList, 0));
FOR i IN 0..DBMS_XMLDOM.GETLENGTH(v_nsAttrNamedNodeMap)-1 LOOP
v_attrTagName
:= DBMS_XMLDOM.GETNODENAME(DBMS_XMLDOM.ITEM(v_nsAttrNamedNodeMap, i));
IF REGEXP_INSTR(v_attrTagName, '^xmlns') = 1 THEN
io_feedXmlNameSpaces
:= io_feedXmlNameSpaces
|| v_attrTagName || '="'
|| DBMS_XMLDOM.GETNODEVALUE(
DBMS_XMLDOM.ITEM(v_nsAttrNamedNodeMap, i))
|| '" ';
END IF;
END LOOP;
END getNameSpaces;
PROCEDURE getFeedEntries(
i_feedType IN VARCHAR2,
i_feedDoc IN DBMS_XMLDOM.DomDocument,
i_feedXmlNameSpaces IN VARCHAR2,
o_feedTitle OUT NOCOPY VARCHAR2,
o_feedUrl OUT NOCOPY VARCHAR2,
o_feedEntryList OUT NOCOPY DBMS_XMLDOM.DomNodeList
)
IS
v_feedSourceXml XMLTYPE;
BEGIN
v_feedSourceXml := DBMS_XMLDOM.GETXMLTYPE(i_feedDoc);
CASE i_feedType
WHEN C_RSS THEN
o_feedEntryList := DBMS_XMLDOM.GETELEMENTSBYTAGNAME(i_feedDoc, 'item');
o_feedTitle := XMLTYPE.EXTRACT(
v_feedSourceXml,
'/rss/channel/title/text()',
i_feedXmlNameSpaces
).GETSTRINGVAL();
o_feedUrl := XMLTYPE.EXTRACT(
v_feedSourceXml,
'/rss/channel/link/text()',
i_feedXmlNameSpaces
).GETSTRINGVAL();
WHEN C_RDF THEN
o_feedEntryList := DBMS_XMLDOM.GETELEMENTSBYTAGNAME(i_feedDoc, 'item');
o_feedTitle := XMLTYPE.EXTRACT(
v_feedSourceXml,
'/rdf:RDF/channel/title/text()',
i_feedXmlNameSpaces
).GETSTRINGVAL();
o_feedUrl := XMLTYPE.EXTRACT(
v_feedSourceXml,
'/rdf:RDF/channel/link/text()',
i_feedXmlNameSpaces
).GETSTRINGVAL();
WHEN C_ATOM THEN
o_feedEntryList := DBMS_XMLDOM.GETELEMENTSBYTAGNAME(i_feedDoc, 'entry');
o_feedTitle := XMLTYPE.EXTRACT(
v_feedSourceXml,
'/feed/title/text()',
i_feedXmlNameSpaces
).GETSTRINGVAL();
o_feedUrl := XMLTYPE.EXTRACT(
v_feedSourceXml,
'/feed/link[@type="text/html"]/@href',
i_feedXmlNameSpaces
).GETSTRINGVAL();
END CASE;
END getFeedEntries;
PROCEDURE drawFeedEntry(
i_feedEntry IN FeedEntryType
)
IS
BEGIN
HTP.TABLEROWOPEN();
HTP.TABLEDATA(
'<a href="'
|| i_feedEntry.link
|| '" style="color:#FF0000" target="_blank">'
|| i_feedEntry.title || ' -- 続きを読む... '
|| '</a>' || TO_CHAR(i_feedEntry.published,'yyyy-mm-dd hh24:mi')
);
HTP.TABLEROWCLOSE();
END drawFeedEntry;
PROCEDURE drawFeedEntries(
i_feedType IN VARCHAR2,
i_feedTitle IN VARCHAR2,
i_feedUrl IN VARCHAR2,
i_feedEntryList IN DBMS_XMLDOM.DomNodeList
)
IS
v_feedEntry FeedEntryType;
v_emptyFeedEntry FeedEntryType;
v_itemNode DBMS_XMLDOM.DomNode;
v_childnode DBMS_XMLDOM.DomNode;
v_feedItemFields DBMS_XMLDOM.DomNodeList;
v_attrNamedNodeMap DBMS_XMLDOM.DomNamedNodeMap;
v_childNodeText VARCHAR2(32767);
BEGIN
HTP.TABLEOPEN();
HTP.TABLEROWOPEN();
HTP.TABLEHEADER(
'<a href="'
|| i_feedUrl
|| '" style="color:#000000" target="_blank">'
|| i_feedTitle
|| '</a>'
|| ' - ' || dbms_xmldom.getlength(i_feedEntryList)|| ' articles.'
);
HTP.TABLEROWCLOSE();
v_feedEntry := v_emptyFeedEntry;
FOR i IN 1..DBMS_XMLDOM.GETLENGTH(i_feedEntryList) LOOP
v_itemNode := DBMS_XMLDOM.ITEM(i_feedEntryList, i-1);
v_feedItemFields := DBMS_XMLDOM.GETCHILDNODES(v_itemNode);
FOR j IN 1..DBMS_XMLDOM.GETLENGTH(v_feedItemFields) LOOP
v_childnode := DBMS_XMLDOM.ITEM(v_feedItemFields, j-1);
v_childNodeText := DBMS_XMLDOM.GETNODEVALUE(
DBMS_XMLDOM.GETFIRSTCHILD(v_childnode)
);
CASE DBMS_XMLDOM.GETNODENAME(v_childnode)
WHEN 'title' THEN
v_feedEntry.title := v_childNodeText;
WHEN 'link' THEN
IF i_feedType = C_ATOM THEN
-- ATOM
v_attrNamedNodeMap := DBMS_XMLDOM.GETATTRIBUTES(v_childnode);
IF DBMS_XMLDOM.GETNODEVALUE(
DBMS_XMLDOM.GETNAMEDITEM(v_attrNamedNodeMap, 'type')
) = 'text/html'
THEN
v_feedEntry.link :=
DBMS_XMLDOM.GETNODEVALUE(
DBMS_XMLDOM.GETNAMEDITEM(v_attrNamedNodeMap, 'href')
);
END IF;
ELSE
-- RSS/RDF
v_feedEntry.link := v_childNodeText;
END IF;
-- RSS 0.9x and RSS 2.0
WHEN 'pubDate' THEN
v_feedEntry.published
:= RFC822_DateStrToTimestampLTZ(v_childNodeText);
-- ATOM 1.0
WHEN 'published' THEN
v_feedEntry.published
:= RFC3339_DateStrToTimestampLTZ(v_childNodeText);
-- RSS/RDF 1.0 , RSS 2.0
WHEN 'dc:date' THEN
v_feedEntry.published
:= RFC3339_DateStrToTimestampLTZ(v_childNodeText);
-- ATOM 0.3
WHEN 'issued' THEN
v_feedEntry.published
:= RFC3339_DateStrToTimestampLTZ(v_childNodeText);
ELSE
NULL; -- nop
END CASE;
END LOOP;
drawFeedEntry(v_feedEntry);
END LOOP;
HTP.TABLECLOSE();
END drawFeedEntries;
--
--
--****************************************************************************
-- Main
--****************************************************************************
BEGIN
v_feedXmlUrl := i_feedXmlUrl;
IF v_feedXmlUrl IS NULL THEN
RAISE_APPLICATION_ERROR(-20003, 'FEEDのURLを指定してください。');
END IF;
getFeedSource(
i_feedXmlUrl => v_feedXmlUrl,
io_feedSourceClob => v_feedSourceClob
);
v_myParser := DBMS_XMLPARSER.NEWPARSER();
DBMS_XMLPARSER.PARSECLOB(v_myParser, v_feedSourceClob);
v_feedDoc := DBMS_XMLPARSER.GETDOCUMENT(v_myParser);
v_rootElementTagName
:= DBMS_XMLDOM.GETTAGNAME(DBMS_XMLDOM.GETDOCUMENTELEMENT(v_feedDoc));
-- v_feedVersion
-- := DBMS_XMLDOM.GETVALUE(
-- DBMS_XMLDOM.GETATTRIBUTENODE(
-- DBMS_XMLDOM.GETDOCUMENTELEMENT(v_feedDoc),
-- C_ATTR_NAME_VERSION
-- )
-- );
getNameSpaces(
i_feedType => v_rootElementTagname,
i_feedDoc => v_feedDoc,
io_feedXmlNameSpaces => v_nsAttrXmlNameSpaces
);
getFeedEntries(
i_feedType => v_rootElementTagname,
i_feedDoc => v_feedDoc,
i_feedXmlNameSpaces => v_nsAttrXmlNameSpaces,
o_feedTitle => v_feedTitle,
o_feedUrl => v_feedUrl,
o_feedEntryList => v_feedEntryList
);
drawFeedEntries(
i_feedType => v_rootElementTagName,
i_feedTitle => v_feedTitle,
i_feedUrl => v_feedUrl,
i_feedEntryList => v_feedEntryList
);
DBMS_LOB.FREETEMPORARY(v_feedSourceClob);
DBMS_XMLDOM.FREEDOCUMENT(v_feedDoc);
DBMS_XMLPARSER.FREEPARSER(v_myParser);
EXCEPTION
WHEN OTHERS THEN
IF v_feedSourceClob IS NOT NULL THEN
DBMS_LOB.FREETEMPORARY(v_feedSourceClob);
END IF;
DBMS_XMLDOM.FREEDOCUMENT(v_feedDoc);
DBMS_XMLPARSER.FREEPARSER(v_myParser);
HTP.TABLEOPEN();
HTP.TABLEROWOPEN();
HTP.TABLEHEADER('RSS Reader error:'||SQLERRM());
HTP.TABLEROWCLOSE();
HTP.TABLECLOSE();
END RSS_READER;
/
show errors
/
まだまだ、早いか?! などと思っていると、あっという間に、クリスマスモードに突入しちゃうんですよね!。
聞いている曲:Take 6 - We wish you a merry christmas
| 固定リンク | 0


コメント