接触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” ,进行测试。这里给出效果图:
![](https://box.kancloud.cn/2016-02-18_56c53c28933c4.jpg)
已经可以完全读取了。不论是单组旧版的,还是多分组新版导出来的opml文件,都可以正常读取。接下来的博文中,会写怎么把结果读取到jsp,会使用树结构来显示。后续版本会加上添加,删除,修改,移动订阅信息及复制订阅信息到组。同时也提供导入,导出功能。这些都会出现在后续博客中。尽请期待。