? ? 接觸java不久,偶有收獲,最近想做一個web版RSS閱讀器來鍛煉一下。手頭有幾個從不同版本的foxmail中導出的opml文件,大家應該都知道,opml文件就是xml格式的。那么就先從這里入手,練習一下使用dom4j讀取xml文件。
? ? 在java程序設計中,尤其是java web開發程序,xml應用頻率超高。Spring、Hibernate、Struts等各種web 框架,MyEclipse、Oracle等IDE,也都主要依托xml。可以說xml對于系統的配置,有著至關重要的作用。而這些也同時增強了系統的靈活性。
? ? 先說一下思路:
? ? 新建一個java web項目,不過暫時沒有使用jsp,servlet。本文只是使用自帶的調試器,先進行測試讀取xml。接下來的博文中,會帶大家一起顯示在已經優化的界面中,并提供大部分的rss閱讀器的功能。
? ? 由于從不同版本的foxmail中導出,文件格式稍有不同,主要分歧是在訂閱分組功能上。有的版本導出來的分組信息是在head/title內容中,body/outline則放的是全部的訂閱信息;有的導出來的分組信息則是在body/outline中title和text屬性中,而詳細的訂閱信息則放在body/outline/outline中。
? ? 我想做的系統可以支持讀取多個opml文件,所以需要一個rss文件列表配置文件【rss_config.xml】,對應一個實體:RssConfigBean.java,主要包含有opml文件路徑信息;分組信息也需要單獨出來,命名為【RssTeamBean.java】,包括title和text兩個屬性和一個訂閱信息的列表。訂閱信息肯定也是獨立的,命名為【RssBean.java】,包括text、title、xmlUrl、htmlUrl、version、type六個屬性。
? ? 首先通過讀取rss_config.xml,拿到所有opml文件路徑,然后循環讀取opml,拿到分組信息及每個分組下的所有詳細訂閱信息,保存到實體中,以供調用顯示。
? ? 光說不管用,直接上代碼:
? ? ①. opml文件
【單分組foxmail6.5.opml】
~~~
<?xml version="1.0"?>
<opml version="1.1">
<head>
<title>六期新博客地址</title>
</head>
<body>
<outline text="丁成云" title="丁成云" type="rss" version="RSS" xmlUrl="http://blog.csdn.net/sundenskyqq/rss/list" htmlUrl="http://blog.csdn.net/sundenskyqq" description=""/>
<outline text="韓正陽" title="韓正陽" type="rss" version="RSS" xmlUrl="http://blog.csdn.net/jiudihanbing/rss/list" htmlUrl="http://blog.csdn.net/jiudihanbing" description=""/>
</body>
</opml>
~~~
【多分組foxmail7.opml】
~~~
<?xml version="1.0" encoding="UTF-8"?>
<opml version="1.0">
<head>
<title>Subscription in Foxmail</title>
</head>
<body>
<outline title="八期" text="八期">
<outline htmlUrl="http://blog.csdn.net/shan9liang" xmlUrl="http://blog.csdn.net/shan9liang/rss/list" version="RSS" type="rss" title="賈琳" text="賈琳的專欄"/>
</outline>
<outline title="隨便看看" text="隨便看看">
<outline htmlUrl="http://blog.csdn.net/blogdevteam" xmlUrl="http://blog.csdn.net/blogdevteam/rss/list" version="RSS" type="rss" title="CSDN 官方博客" text="CSDN 官方博客"/>
<outline htmlUrl="http://blog.csdn.net/zhoufoxcn" xmlUrl="http://blog.csdn.net/zhoufoxcn/rss/list" version="RSS" type="rss" title="周公的專欄" text="周公的專欄"/>
</outline>
<outline title="提高班" text="提高班">
<outline htmlUrl="http://sxyandapp.blog.163.com" xmlUrl="http://sxyandapp.blog.163.com/rss" version="RSS" type="rss" title="石小永" text="石小永"/>
<outline htmlUrl="http://blog.csdn.net/qiulongtianshi" xmlUrl="http://blog.csdn.net/qiulongtianshi/rss/list" version="RSS" type="rss" title="郭校林" text="郭校林"/>
</outline>
</body>
</opml>
~~~
②. 在src中新建rss文件列表配置文件
【rss_config.xml】
~~~
<?xml version="1.0" encoding="UTF-8"?>
<rss>
<rss-list>
<rss-name>單分組foxmail6.5</rss-name>
<rss-path>\rss\單分組foxmail6.5.opml</rss-path>
</rss-list>
<rss-list>
<rss-name>多分組foxmail7</rss-name>
<rss-path>\rss\多分組foxmail7.opml</rss-path>
</rss-list>
</rss>
~~~
③. 新建包com.tgb.rssreader.bean,添加3個java文件:
【RssBean.java】詳細訂閱信息
~~~
package com.tgb.rssreader.bean;
/**
* 詳細訂閱信息
* @author Longxuan
*
*/
public class RssBean {
/**
* 顯示名稱
*/
private String text;
/**
* 標題
*/
private String title;
/**
* rss訂閱地址
*/
private String htmlUrl;
/**
* rss訂閱地址
*/
private String xmlUrl;
/**
* 版本
*/
private String version;
/**
* 類型
*/
private String type;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getHtmlUrl() {
return htmlUrl;
}
public void setHtmlUrl(String htmlUrl) {
this.htmlUrl = htmlUrl;
}
public String getXmlUrl() {
return xmlUrl;
}
public void setXmlUrl(String xmlUrl) {
this.xmlUrl = xmlUrl;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
~~~
【RssConfigBean.java】?rss配置信息
~~~
package com.tgb.rssreader.bean;
/**
* rss配置信息
* @author Longxuan
*
*/
public class RssConfigBean {
/**
* 分組名稱
*/
private String name;
/**
* 路徑
*/
private String path;
public String getPath() {
return path;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setPath(String path) {
this.path = path;
}
}
~~~
【RssTeamBean.java】rss分組信息
~~~
package com.tgb.rssreader.bean;
import java.util.List;
/**
* rss分組信息
* @author Longxuan
*
*/
public class RssTeamBean {
/**
* 分組標題
*/
private String title;
/**
* 分組名稱
*/
private String text;
private List<RssBean> rssBeanList ;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public List<RssBean> getRssBeanList() {
return rssBeanList;
}
public void setRssBeanList(List<RssBean> rssBeanList) {
this.rssBeanList = rssBeanList;
}
}
~~~
④. 新建包com.tgb.rssreader.manager,添加2個文件:
【RssConfigMgr.java】rss文件列表配置管理器
~~~
package com.tgb.rssreader.manager;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.tgb.rssreader.bean.RssConfigBean;
/**
* rss文件列表配置管理器
* @author Longxuan
*
*/
public class RssConfigMgr {
/**
* 讀取rss文件列表配置信息
* @return
*/
public List<RssConfigBean> getRssConfig() {
List<RssConfigBean> list = new ArrayList<RssConfigBean>();
RssConfigBean rssConfigBean = null;
InputStream is = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("rss_config.xml");
if (is == null) {
//System.out.println("找不到該文件");
//return null;
throw new RuntimeException("找不到rss_config.xml文件");
}
try {
// 讀取并解析XML文檔
// SAXReader就是一個管道,用一個流的方式,把xml文件讀出來
SAXReader reader = new SAXReader();
// 下面的是通過解析xml字符串的
Document doc = reader.read(is);
Element rootElt = doc.getRootElement(); // 獲取根節點
//System.out.println("根節點:" + rootElt.getName()); // 拿到根節點的名稱
Iterator<?> iter = rootElt.elementIterator("rss-list"); // 獲取根節點下的子節點rss-list
// 遍歷rss-list節點
while (iter.hasNext()) {
Element recordEle = (Element) iter.next();
String name = recordEle.elementTextTrim("rss-name"); // 拿到rss-list節點下的子節點name值
//System.out.println("name:" + name);
String path = recordEle.elementTextTrim("rss-path"); // 拿到rss-list節點下的子節點path值
//System.out.println("path:" + path);
rssConfigBean = new RssConfigBean();
//保存到rssConfigBean中
rssConfigBean.setName(name);
rssConfigBean.setPath(path);
//保存到list中
list.add(rssConfigBean);
}
} catch (DocumentException e) {
e.printStackTrace();
}
return list;
}
}
~~~
【ReadXML.java】讀取xml文件
~~~
package com.tgb.rssreader.manager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.tgb.rssreader.bean.RssBean;
import com.tgb.rssreader.bean.RssConfigBean;
import com.tgb.rssreader.bean.RssTeamBean;
/**
* 讀取xml文件
* @author Longxuan
*
*/
public class ReadXML {
// rss分組訂閱列表
private List<RssTeamBean> rssTeamBeanList = new ArrayList<RssTeamBean>();
/**
* 讀取rss文件列表
*/
public void ReadRss() {
// rss文件列表配置信息實體
RssConfigMgr rssConfigMgr = new RssConfigMgr();
List<RssConfigBean> list = rssConfigMgr.getRssConfig();
String errText = "";// 記錄錯誤信息
// 循環讀取rss文件列表
for (RssConfigBean rssConfig : list) {
// System.out.println(rssConfig.getName() + "----" +
// rssConfig.getPath());
try {
// 讀取rss文件內容
ReadRss(System.getProperty("user.dir")+ rssConfig.getPath());
} catch (Exception e) {
errText += e.getMessage();
}
}
}
/**
* 讀取ompl文件
*
* @param filePath
*/
private void ReadRss(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
// System.out.println("找不到【" + filePath + "】文件");
// return;
throw new RuntimeException("找不到【" + filePath + "】文件");
}
try {
// 讀取并解析XML文檔
// SAXReader就是一個管道,用一個流的方式,把xml文件讀出來
SAXReader reader = new SAXReader();
FileInputStream fis = new FileInputStream(file);
// 下面的是通過解析xml字符串的
Document doc = reader.read(fis);
// 獲取根節點
Element rootElt = doc.getRootElement(); // 獲取根節點
// System.out.println("根節點:" + rootElt.getName()); // 拿到根節點的名稱
// 獲取head/title節點
Element titleElt = (Element) rootElt.selectSingleNode("head/title");// 獲取head節點下的子節點title
// 獲取分組名稱
String title = titleElt.getTextTrim();
// 獲取body節點
Element bodyElt = (Element) rootElt.selectSingleNode("body");
// 獲取body下的第一個outline節點
Element outlineElt = (Element) bodyElt.selectSingleNode("outline");
// 判斷該outlineElt節點的屬性數量,>2說明是詳細博客訂閱信息,否則則是分組信息。
if (outlineElt.attributes().size() > 2) { // 詳細博客訂閱信息
// 實例化 RSS分組實體
RssTeamBean rssTeamBean = new RssTeamBean();
// 獲取body節點下的outline節點
Iterator<?> iter = bodyElt.elementIterator("outline");
// 輸出分組名稱
// System.out.println("分組名稱:" + title);
// 記錄分組名稱
rssTeamBean.setTitle(title);
rssTeamBean.setText(title);
// 實例化訂閱列表
List<RssBean> rssBeanList = new ArrayList<RssBean>();
// 獲取詳細博客訂閱信息
ReadBlogsInfo(iter, rssBeanList);
// 設置訂閱列表到分組中
rssTeamBean.setRssBeanList(rssBeanList);
// 添加分組到rss分組訂閱列表
rssTeamBeanList.add(rssTeamBean);
} else { // 分組信息
// 獲取body節點下的outline節點
Iterator<?> iter = bodyElt.elementIterator("outline");
while (iter.hasNext()) {
// 讀取outline節點下的所有outline信息,每條信息都是一條訂閱記錄
Element TeamElt = (Element) iter.next();
// 實例化 RSS分組實體
RssTeamBean rssTeamBean = new RssTeamBean();
// 重新獲取分組名稱
title = TeamElt.attributeValue("title");
String text = TeamElt.attributeValue("text");
// System.out.println("分組title:" + title + " text:" +
// text);
// 記錄分組名稱和顯示名稱
rssTeamBean.setTitle(title);
rssTeamBean.setText(text);
// 實例化訂閱列表
List<RssBean> rssBeanList = new ArrayList<RssBean>();
// 獲取body節點下的outline節點
Iterator<?> iterRss = TeamElt.elementIterator("outline");
// 獲取詳細博客訂閱信息
ReadBlogsInfo(iterRss, rssBeanList);
// 設置訂閱列表到分組中
rssTeamBean.setRssBeanList(rssBeanList);
// 添加分組到rss分組訂閱列表
rssTeamBeanList.add(rssTeamBean);
}
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 讀取當前組博客訂閱信息
*
* @param iter
* 當前節點的子節點迭代器
* @param rssBeanList
* 訂閱列表
*/
private void ReadBlogsInfo(Iterator<?> iter, List<RssBean> rssBeanList) {
// 遍歷所有outline節點,每個節點都是一條訂閱信息
while (iter.hasNext()) {
RssBean rssBean = new RssBean();
Element outlineElt = (Element) iter.next();
String htmlUrl = outlineElt.attributeValue("htmlUrl"); // 拿到當前節點的htmlUrl屬性值
String xmlUrl = outlineElt.attributeValue("xmlUrl"); // 拿到當前節點的xmlUrl屬性值
String version = outlineElt.attributeValue("version"); // 拿到當前節點的version屬性值
String type = outlineElt.attributeValue("type"); // 拿到當前節點的type屬性值
String outlineTitle = outlineElt.attributeValue("title"); // 拿到當前節點的title屬性值
String outlineText = outlineElt.attributeValue("text"); // 拿到當前節點的text屬性值
// System.out.print("<outline htmlUrl=\"" + htmlUrl + "\" ");
// System.out.print("xmlUrl=\"" + xmlUrl + "\" ");
// System.out.print("version=\"" + version + "\" ");
// System.out.print("type=\"" + type + "\" ");
// System.out.print("title=\"" + outlineTitle + "\" ");
// System.out.println("text=\"" + outlineText + "\" />");
rssBean.setHtmlUrl(htmlUrl);
rssBean.setXmlUrl(xmlUrl);
rssBean.setVersion(version);
rssBean.setType(type);
rssBean.setTitle(outlineTitle);
rssBean.setText(outlineText);
rssBean.setText(outlineText);
// 將每條訂閱信息,存放到訂閱列表中
rssBeanList.add(rssBean);
}
}
/**
* 獲取Rss分組訂閱列表
*
* @return
*/
public List<RssTeamBean> getRssTemBeanList() {
return rssTeamBeanList;
}
public static void main(String[] args) {
ReadXML readXML = new ReadXML();
readXML.ReadRss();
List<RssTeamBean> rssTemBeanList = readXML.getRssTemBeanList();
for (RssTeamBean rssTeamBean : rssTemBeanList) {
System.out.println("【分組title:" + rssTeamBean.getTitle() + " text:"+ rssTeamBean.getText()+"】");
for (RssBean rssBean : rssTeamBean.getRssBeanList()) {
System.out.print("<outline htmlUrl=\"" + rssBean.getHtmlUrl() + "\" ");
//System.out.print("xmlUrl=\"" + rssBean.getXmlUrl() + "\" ");
System.out.print("version=\"" + rssBean.getVersion() + "\" ");
System.out.print("type=\"" + rssBean.getType() + "\" ");
System.out.print("title=\"" + rssBean.getTitle() + "\" ");
System.out.println("text=\"" + rssBean.getText() + "\" />");
}
System.out.println("-------------------------------------------------");
}
}
}
~~~
? ? 由于沒有使用jsp進行顯示,所以要想看效果,直接右鍵main函數,選擇“Run As”-》“1 Java Application” ,進行測試。這里給出效果圖:

? ? 已經可以完全讀取了。不論是單組舊版的,還是多分組新版導出來的opml文件,都可以正常讀取。接下來的博文中,會寫怎么把結果讀取到jsp,會使用樹結構來顯示。后續版本會加上添加,刪除,修改,移動訂閱信息及復制訂閱信息到組。同時也提供導入,導出功能。這些都會出現在后續博客中。盡請期待。