RSS阅读器(一)——dom4j读取xml(opml)文件

      接触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,会使用树结构来显示。后续版本会加上添加,删除,修改,移动订阅信息及复制订阅信息到组。同时也提供导入,导出功能。这些都会出现在后续博客中。尽请期待。


 

 

 

posted on 2013-07-30 19:35  you Richer  阅读(524)  评论(0编辑  收藏  举报