[toc]
之前想写一个JAXB解析xml与实体类转换的,但是发现JAXB有一定的局限性,有时,在解析非标准xml中的属性值时,不能够获取到其中的值,很奇怪的是,JAXB是jdk中自带的API,竟然在AndroidStudio环境中竟然不能使用,引入jar包也会报错,后索性改为用SAX解析,并对其进行了一定的封装,只需要传入几个简单的参数即可得到想要的实体类。
如果你的需求是根据解析xml返回一个简单对象集合,那么来这就对了。
何为简单对象,即这个对象的成员类型属于基本数据类型,当然了Date也可以,你只需要添加相关注解将字符串转换成date就行了;不含有自定义类
1. 简单介绍SAX
SAX(Simple API for XML)解析器是一种基于事件的解析器,它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。
SAX解析器的优点是解析速度快,占用内存少。非常适合在Android移动设备中使用
2. 列出要解析的数据(数据来源:http://wcf.open.cnblogs.com/news/hot/10)
- <Feed xmlns="http://www.w3.org/2005/Atom">
- <title type="text">博客园新闻频道</title>
- <id>uuid:400dd255-fe1f-4cc3-bebd-bbe2e47f2c0f;id=49744</id>
- <updated>2017-04-09T10:13:38Z</updated>
- <link href="http://news.cnblogs.com/" />
- <entry>
- <id>566495</id>
- <title type="text">老外两张漫画实力黑Linux版sql Server</title>
- <summary type="text">
- Linux 版 sql Server(一)在 Linux 内核之中,大家正在静静的等待进程的创建。每个创建的进程会被分配一个进程 ID (PID)。在那个 Apache 进程高高兴兴的走出去之后,下一位却被要求创建 Linux 版的 sql Server,这简直让人气的跳...
- </summary>
- <published>2017-04-06T09:22:33+08:00</published>
- <updated>2017-04-09T10:13:38Z</updated>
- <link rel="alternate" href="http://news.cnblogs.com/n/566495/" />
- <diggs>16</diggs>
- <views>4542</views>
- <comments>20</comments>
- <topic />
- <topicIcon>http://images0.cnblogs.com/news_topic///images0.cnblogs.com/news_topic/sqlserver.gif</topicIcon>
- <sourceName>linux.cn</sourceName>
- </entry>
- <entry>
- <id>566335</id>
- <title type="text">IBM都叫停SOHO办公了!创业公司还要犯这大忌?</title>
- <summary type="text">
- SOHO 办公一度是个十分流行的概念。据美国民意调查机构 Gallup poll 统计,美国每四个人中就有一个人选择 SOHO 办公。中国创业者最崇拜的就是自由式、咖啡厅式的谷歌办公环境。但现在,即使是一些以创新和开放著称的大公司,也开始逐渐召回自己的 SOHO 员工,让他们...
- </summary>
- <published>2017-04-04T16:50:41+08:00</published>
- <updated>2017-04-09T10:13:38Z</updated>
- <link rel="alternate" href="http://news.cnblogs.com/n/566335/" />
- <diggs>6</diggs>
- <views>3866</views>
- <comments>11</comments>
- <topic />
- <topicIcon>http://images0.cnblogs.com/news_topic///images0.cnblogs.com/news_topic/ibm.gif</topicIcon>
- <sourceName>虎嗅网</sourceName>
- </entry>
- </Feed>
3. 从上面数据我们可以看出,主要是要entry中的值,所以根据entry内容,我们得到以下实体类:
- /** * Created by 郑明亮 on 2017/4/8 20:16. */
-
- public class BKYNews {
-
- private String id;
-
- private String title;
-
- /** * 总结,新闻概述 */
- private String summary;
-
- /** * 发布时间 */
- private String published;
-
- /** * 更新时间 */
- private String updated;
-
- private String link_href;
- /** * 推荐次数(点赞次数) */
- private String diggs;
- /** * 浏览量 */
- private String views;
-
- /** * 评论数 */
- private String comments;
-
- /** * 话题图标,这里的网址有问题,这里的网址有一部分是重复的,只需要截取“///”之后的内容然后前篇拼接“http://”即可 */
- private String topicIcon;
-
- /** * 新闻来源 */
- private String sourceName;
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public String getSummary() {
- return summary;
- }
-
- public void setSummary(String summary) {
- this.summary = summary;
- }
-
- public String getPublished() {
- return published;
- }
-
- public void setPublished(String published) {
- this.published = published;
- }
-
- public String getUpdated() {
- return updated;
- }
-
- public void setUpdated(String updated) {
- this.updated = updated;
- }
-
-
- public String getDiggs() {
- return diggs;
- }
-
- public void setDiggs(String diggs) {
- this.diggs = diggs;
- }
-
- public String getViews() {
- return views;
- }
-
- public void setViews(String views) {
- this.views = views;
- }
-
- public String getComments() {
- return comments;
- }
-
- public void setComments(String comments) {
- this.comments = comments;
- }
-
- public String getTopicIcon() {
- return topicIcon;
- }
-
- public void setTopicIcon(String topicIcon) {
- this.topicIcon = topicIcon;
- }
-
- public String getSourceName() {
- return sourceName;
- }
-
- public void setSourceName(String sourceName) {
- this.sourceName = sourceName;
- }
-
- public String getLink_href() {
- return link_href;
- }
-
- public void setLink_href(String link_href) {
- this.link_href = link_href;
- }
-
- @Override
- public String toString() {
- return "BKYNews{" +
- "id='" + id + '\'' +
- ",title='" + title + '\'' +
- ",summary='" + summary + '\'' +
- ",published='" + published + '\'' +
- ",updated='" + updated + '\'' +
- ",link='" + link_href + '\'' +
- ",diggs='" + diggs + '\'' +
- ",views='" + views + '\'' +
- ",comments='" + comments + '\'' +
- ",topicIcon='" + topicIcon + '\'' +
- ",sourceName='" + sourceName + '\'' +
- '}';
- }
- }
4. 给出的实体类的规则是:
5. 给出SAX最最重要的解析过程,这个是个工具类中的内容,可直接拷贝,无须修改
- /** * @author 郑明亮 * @Email zhengmingliang911@gmail.com * @Time 2017年4月9日 下午2:58:36 * @Description <p>用于解析xml为实体类的处理器 </P> * @version 1.0 */
- class XMlHandler<T> extends DefaultHandler{
- String rootElemntName;
- Map<String,String> dataMap;
- StringBuilder stringBuilder;
- List<T> dataList;
- T data;
- Class<T> clz;
- private Map<String,Class> attrs;
-
- /** * @author 郑明亮 * @Email zhengmingliang911@gmail.com * @Time 2017年4月9日 下午3:13:43 * @Description <p> 当前标签名称 </P> * @version 1.0 */
- private String currentTag;
- /** * 要解析的单个实体的根元素名称 * @param rootElemntName * @throws IllegalAccessException * @throws InstantiationException */
- XMlHandler(String rootElemntName,Class<T> clz,Map<String,Class> attrsMap) {
- this.rootElemntName = rootElemntName;
- this.clz = clz;
- this.attrs = attrsMap;
- }
-
- @Override
- public void startDocument() throws SAXException {
- super.startDocument();
- dataMap = new HashMap<String,String>();
- stringBuilder = new StringBuilder();
- dataList = new ArrayList<>();
- }
-
- @Override
- public void startElement(String uri,String localName,String qName,Attributes attributes) throws SAXException {
- // TODO Auto-generated method stub
- super.startElement(uri,localName,qName,attributes);
- //赋值给当前标签名称
- currentTag = qName;
- if (rootElemntName.equals(qName)) {
- try {
- data = clz.newInstance();
-
- } catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- }
- //每次对一个标签解析前,都先置为空
- stringBuilder.setLength(0);
- //如果某个标签中有属性,则将其保存到map中,保存规则:key = “标签名称_属性名称” value = “属性值”
- if(attributes != null && dataMap != null){
- for (int i = 0; i < attributes.getLength(); i++) {
- dataMap.put(qName+"_"+attributes.getQName(i),attributes.getValue(i));
- }
-
- }
-
-
- }
-
- @Override
- public void characters(char[] ch,int start,int len) throws SAXException {
- // TODO Auto-generated method stub
- super.characters(ch,start,len);
- stringBuilder.append(ch,len);
- Field[] fields = clz.getDeclaredFields();
- try {
- if (StringUtils.isNotEmpty(currentTag) && data != null) {
- for (Field field : fields) {
- String name = field.getName();
- if (currentTag.equals(name)) {
- name = name.substring(0,1).toUpperCase() + name.substring(1);
-
- Method method = data.getClass().getMethod("set"+name,field.getType());
- method.invoke(data,stringBuilder.toString());
- }
- }
- }
-
- } catch (NoSuchMethodException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (SecurityException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- }
-
- @Override
- public void endElement(String uri,String qName) throws SAXException {
- super.endElement(uri,qName);
- if (rootElemntName.equals(qName)) {
- try {
- if (attrs != null) {
-
- for (String attrName : attrs.keySet()) {
- String methodName = "set"+attrName.substring(0,1).toUpperCase()+attrName.substring(1);
- Method method = data.getClass().getMethod(methodName,attrs.get(attrName));
- method.invoke(data,dataMap.get(attrName));
- }
- }
- } catch (NoSuchMethodException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (SecurityException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- dataList.add(data);
- }
-
- }
-
- @Override
- public void endDocument() throws SAXException {
- System.out.println("解析结束:"+dataList);
- super.endDocument();
- }
-
-
- public List<T> getDataList(){
- return dataList;
- }
-
- public T getData(){
- return data;
- }
- }
6. 工具类:主要看工具类的最后一个方法:parseXml2Bean,前面方法均是JAXB对xml与对象转换的方法
- import java.io.ByteArrayInputStream;
- import java.io.FileWriter;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.StringReader;
- import java.io.StringWriter;
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
-
- import javax.xml.bind.JAXBContext;
- import javax.xml.bind.JAXBException;
- import javax.xml.bind.Marshaller;
- import javax.xml.bind.Unmarshaller;
- import javax.xml.parsers.ParserConfigurationException;
- import javax.xml.parsers.SAXParser;
- import javax.xml.parsers.SAXParserFactory;
-
- import org.junit.Test;
- import org.xml.sax.Attributes;
- import org.xml.sax.SAXException;
- import org.xml.sax.helpers.DefaultHandler;
-
- import top.wys.developerclub.model.SysUser;
- import top.wys.developerclub.test.BKYAticle;
-
- public class XmlUtils {
-
-
- /** * @author 郑明亮 * @Time 2017年4月1日19:04:50 * @Description <p> * 将实体类直接转换成xml * </p> * @param t * 要转换成xml的对象 * @return xml字符串 */
- public static <T> String createXmlFromBean(T t) {
- String xml = "";
- if (t == null) {
- return xml;
- } else {
- try (StringWriter write = new StringWriter()) {
- JAXBContext context = JAXBContext.newInstance(t.getClass());
- Marshaller marshaller = context.createMarshaller();
- marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true);
- marshaller.marshal(t,write);
- xml = write.toString();
- } catch (JAXBException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
- return xml;
- }
- }
- /** * @author 郑明亮 * @Time 2017年4月1日19:04:50 * @Description <p> * 将实体类直接转换成xml,要转换的类必需加油@XmlRootElement注解 * </p> * @param t * 要转换成xml的对象 * @param format 是否格式化输出,{@code true}格式化输出,{@code false} 不进行格式化 * @return xml字符串 */
- public static <T> String createXmlFromBean(T t,boolean format) {
- String xml = "";
- if (t == null) {
- return xml;
- } else {
- try (StringWriter write = new StringWriter()) {
- // 利用jdk中自带的转换类实现
- JAXBContext context = JAXBContext.newInstance(t.getClass());
- Marshaller marshaller = context.createMarshaller();
- // 格式化xml输出的格式
- marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,format);
- // 将对象转换成xml写入到StringWriter中
- marshaller.marshal(t,write);
- xml = write.toString();
- } catch (JAXBException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
- return xml;
- }
- }
- /** * @author 郑明亮 * @Time 2017年4月1日19:04:50 * @Description <p> * 将实体类直接转换成xml,要转换的类必需加油@XmlRootElement注解 * </p> * @param t 要转换成xml的对象 * * @param filePath 要保存到的文件路径 * @return xml字符串 */
- public static <T> void createXmlToFile(T t,String filePath) {
- try (FileWriter write = new FileWriter(filePath)) {
- // 利用jdk中自带的转换类实现
- JAXBContext context = JAXBContext.newInstance(t.getClass());
- Marshaller marshaller = context.createMarshaller();
- // 格式化xml输出的格式
- marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true);
- // 将对象转换成xml写入到StringWriter中
- marshaller.marshal(t,write);
- } catch (JAXBException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
- }
-
- @SuppressWarnings("unchecked")
- public static <T> T getBeanFromXml(String xml,Class<T> clz) {
- T t = null;
- try (StringReader reader = new StringReader(xml);) {
- JAXBContext context = JAXBContext.newInstance(clz);
- Unmarshaller unmarshal = context.createUnmarshaller();
- t = (T) unmarshal.unmarshal(reader);
- } catch (JAXBException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return t;
- }
-
- @SuppressWarnings("unchecked")
- public static <T> T getBeanFromXml(InputStream is,Class<T> clz) {
- T t = null;
- try{
- JAXBContext context = JAXBContext.newInstance(SysUser.class);
- Unmarshaller unmarshal = context.createUnmarshaller();
- t = (T) unmarshal.unmarshal(is);
- } catch (JAXBException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return t;
- }
- /** * @author 郑明亮 * @Email zhengmingliang911@gmail.com * @Time 2017年4月9日 下午6:32:02 * @Description <p> 将xml转换为指定对象 </P> * @param xml 要解析的xml数据 * @param rootElemntName 要解析的内容的根标签名称 * @param clz 要转换成的实体类, * @param attrs key值为要解析的xml标签中的属性值, value 值为属性值类型 * @return */
- public <T> List<T> parseXml2Bean(String xml,String rootElemntName,Class> attrs){
-
- XMlHandler<T> handler = null;
- try(ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes());) {
- handler = new XMlHandler<>(rootElemntName,clz,attrs);
- SAXParserFactory factory = SAXParserFactory.newInstance();
- SAXParser parser = factory.newSAXParser();
- parser.parse(stream,handler);
- System.out.println(handler.getDataList());
- } catch (ParserConfigurationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (SAXException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return handler.getDataList();
- }
- }
7. 测试类:
- @Test
- public void testXML2Model1() throws IOException {
- String xml = HttpUtils.getHttpData("http://wcf.open.cnblogs.com/news/hot/10");
- Map<String,Class> map = new HashMap<>();
- map.put("link_href",String.class);
- List<BKYAticle> list = parseXml2Bean(xml,"entry",BKYAticle.class,map);
-
- }
8. 效果图
点次查看完整大图(一定要找到中间那条线,然后在上面放大查看哦)