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

RSS阅读器(一)——dom4j读取xml(opml)文件的更多相关文章

  1. Web版RSS阅读器(二)——使用dTree树形加载rss订阅分组列表

    在上一边博客<Web版RSS阅读器(一)——dom4j读取xml(opml)文件>中已经讲过如何读取rss订阅文件了.这次就把订阅的文件读取到页面上,使用树形结构进行加载显示. 不打算使用 ...

  2. C#版简易RSS阅读器

    C#版简易RSS阅读器.由VB版修改完成,感谢aowind的技术支持! 源代码: using System; using System.Drawing; using System.Collection ...

  3. [UWP]涨姿势UWP源码——极简的RSS阅读器

    涨姿势UWP,一个开源的RSS阅读器,一个纯粹的项目,一个有道德的APP,一个脱离了低级趣味的作者,一些有益于人民的代码.骚年,还等什么,来涨点姿势吧! 该项目代码可能会引起部分人群的不适,敏感人群请 ...

  4. 网易新闻RSS阅读器

    首先需要分析网易RSS订阅中心的网页布局情况. 网易RSS订阅中心:http://www.163.com/rss/ 你会发现RSS文件由一个<channel>元素及其子元素组成,除了频道本 ...

  5. Web版RSS阅读器(三)——解析在线Rss订阅

    上篇博客<Web版RSS阅读器(二)——使用dTree树形加载rss订阅分组列表>已经写到读取rss订阅列表了,今天就说一下,当获取一条在线rss订阅的信息,怎么去解析它,从而获取文章或资 ...

  6. RSS阅读器

    RSS阅读器(Really Simple Syndication)是一种软件/程序,实质都是为了方便地读取RSS和Atom文档.大概就是实现了订阅式阅读,推送用户感兴趣的新闻,博客等(比如等某位博主更 ...

  7. RSS阅读器python实现概述

    这边简单说一下最近倒腾的RSS阅读器的小东东,RSS阅读器估计很多人用过或者自己动手实现过.首先wudagang0123多年前提供的一个示例:http://bbs.chinaunix.net/foru ...

  8. RSS阅读器&BT sync

    ①RSS阅读器? 答:RSS阅读器是一种软件或是说一个程序,这种软件可以自由读取RSS和Atom两种规范格式的文档,且这种读取RSS和Atom文档的软件有多个版本,由不同的人或公司开发,有着不同的名字 ...

  9. DOM4J读取XML文件

    最近在做DRP的项目,其中涉及到了读取配置文件,用到了DOM4J,由于是刚开始接触这种读取xml文件的技术,好奇心是难免的,于是在网上又找了一些资料,这里就结合找到的资料来谈一下读取xml文件的4中方 ...

随机推荐

  1. next_permutation()函数 和 prev_permutation() 按字典序求全排列

    next_permutation功能:    求一个排序的下一个排列的函数,可以遍历全排列,要包含头文件<algorithm> 与之完全相反的函数还有prev_permutation 这个 ...

  2. UVa 11609 (计数 公式推导) Teams

    n个人里选k个人有C(n, k)中方法,再从里面选一人当队长,有k中方法. 所以答案就是 第一步的变形只要按照组合数公式展开把n提出来即可. #include <cstdio> typed ...

  3. 微信分享自定义图片标题摘要-微信官方API

    我们平时在使用微信内置浏览器打开网页想要分享给好友或者发到朋友圈的时候经常会遇到这样的问题, 别人的网页分享的时候是这样的: 而我们自己的网页分享后这这样的: 看到有人说不做任何设置,微信分享时会自动 ...

  4. UVALive 3486/zoj 2615 Cells(栈模拟dfs)

    这道题在LA是挂掉了,不过还好,zoj上也有这道题. 题意:好大一颗树,询问父子关系..考虑最坏的情况,30w层,2000w个点,询问100w次,貌似连dfs一遍都会TLE. 安心啦,这肯定是一道正常 ...

  5. UVA 10537 The Toll! Revisited 过路费(最短路,经典变形)

    题意:给一个无向图,要从起点s运送一批货物到达终点e,每个点代表城镇/乡村,经过城镇需要留下(num+19)/20的货物,而经过乡村只需要1货物即可.现在如果要让p货物到达e,那么从起点出发最少要准备 ...

  6. Window 下 VFW 视频采集与显示

    引言 经过几天的努力终于将VFW视频采集与显示功能完整实现了,不得不说网上对这方面完整的详细讲解文章是在太少了.所以就要本人来好好总结一下让后来者不再像我一样折腾好久.在本文中我将详细讲解VFW视频采 ...

  7. java/python中获取当前系统时间,并与字符串相互转换格式,或者转化成秒数,天数等整数

    java转换成秒数 Date类有一个getTime()可以换回秒数,例如: public class DateToSecond { public static void main(String[] a ...

  8. poj 3181 Dollar Dayz

    题意:给定一个数p,要求用K种币值分别为1,2,3...K的硬币组成p,问方案数,1,2,2和2,2,1算一种方案即与顺序无关,n <= 1000,k <= 100// 用完全背包做了 这 ...

  9. B树索引和位图索引的区别!

    B树索引主键和唯一性约束字段的B树索引,效率几乎和海量数据没有关系. 键值重复率低的字段比较适合使用B树索引. 位图索引键值重复率高的字段比较适合使用位图索引.count.and.or.in这些特定的 ...

  10. 【WEB小工具】BaseServlet—一个Servlet处理多个请求

    package cn.itcast.test.web.servlet; import java.io.IOException; import java.io.PrintWriter; import j ...