Xml解析之Sax解析(传入xml即可得到实体类集合)

前端之家收集整理的这篇文章主要介绍了Xml解析之Sax解析(传入xml即可得到实体类集合)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

[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

  1. <Feed xmlns="http://www.w3.org/2005/Atom">
  2. <title type="text">博客园新闻频道</title>
  3. <id>uuid:400dd255-fe1f-4cc3-bebd-bbe2e47f2c0f;id=49744</id>
  4. <updated>2017-04-09T10:13:38Z</updated>
  5. <link href="http://news.cnblogs.com/" />
  6. <entry>
  7. <id>566495</id>
  8. <title type="text">老外两张漫画实力黑Linux版sql Server</title>
  9. <summary type="text">
  10. Linux 版 sql Server(一)在 Linux 内核之中,大家正在静静的等待进程的创建。每个创建的进程会被分配一个进程 ID (PID)。在那个 Apache 进程高高兴兴的走出去之后,下一位却被要求创建 Linux 版的 sql Server,这简直让人气的跳...
  11. </summary>
  12. <published>2017-04-06T09:22:33+08:00</published>
  13. <updated>2017-04-09T10:13:38Z</updated>
  14. <link rel="alternate" href="http://news.cnblogs.com/n/566495/" />
  15. <diggs>16</diggs>
  16. <views>4542</views>
  17. <comments>20</comments>
  18. <topic />
  19. <topicIcon>http://images0.cnblogs.com/news_topic///images0.cnblogs.com/news_topic/sqlserver.gif</topicIcon>
  20. <sourceName>linux.cn</sourceName>
  21. </entry>
  22. <entry>
  23. <id>566335</id>
  24. <title type="text">IBM都叫停SOHO办公了!创业公司还要犯这大忌?</title>
  25. <summary type="text">
  26. SOHO 办公一度是个十分流行的概念。据美国民意调查机构 Gallup poll 统计,美国每四个人中就有一个人选择 SOHO 办公。中国创业者最崇拜的就是自由式、咖啡厅式的谷歌办公环境。但现在,即使是一些以创新和开放著称的大公司,也开始逐渐召回自己的 SOHO 员工,让他们...
  27. </summary>
  28. <published>2017-04-04T16:50:41+08:00</published>
  29. <updated>2017-04-09T10:13:38Z</updated>
  30. <link rel="alternate" href="http://news.cnblogs.com/n/566335/" />
  31. <diggs>6</diggs>
  32. <views>3866</views>
  33. <comments>11</comments>
  34. <topic />
  35. <topicIcon>http://images0.cnblogs.com/news_topic///images0.cnblogs.com/news_topic/ibm.gif</topicIcon>
  36. <sourceName>虎嗅网</sourceName>
  37. </entry>
  38. </Feed>

3. 从上面数据我们可以看出,主要是要entry中的值,所以根据entry内容,我们得到以下实体类:

  1. /** * Created by 郑明亮 on 2017/4/8 20:16. */
  2.  
  3. public class BKYNews {
  4.  
  5. private String id;
  6.  
  7. private String title;
  8.  
  9. /** * 总结,新闻概述 */
  10. private String summary;
  11.  
  12. /** * 发布时间 */
  13. private String published;
  14.  
  15. /** * 更新时间 */
  16. private String updated;
  17.  
  18. private String link_href;
  19. /** * 推荐次数(点赞次数) */
  20. private String diggs;
  21. /** * 浏览量 */
  22. private String views;
  23.  
  24. /** * 评论数 */
  25. private String comments;
  26.  
  27. /** * 话题图标,这里的网址有问题,这里的网址有一部分是重复的,只需要截取“///”之后的内容然后前篇拼接“http://”即可 */
  28. private String topicIcon;
  29.  
  30. /** * 新闻来源 */
  31. private String sourceName;
  32.  
  33. public String getId() {
  34. return id;
  35. }
  36.  
  37. public void setId(String id) {
  38. this.id = id;
  39. }
  40.  
  41. public String getTitle() {
  42. return title;
  43. }
  44.  
  45. public void setTitle(String title) {
  46. this.title = title;
  47. }
  48.  
  49. public String getSummary() {
  50. return summary;
  51. }
  52.  
  53. public void setSummary(String summary) {
  54. this.summary = summary;
  55. }
  56.  
  57. public String getPublished() {
  58. return published;
  59. }
  60.  
  61. public void setPublished(String published) {
  62. this.published = published;
  63. }
  64.  
  65. public String getUpdated() {
  66. return updated;
  67. }
  68.  
  69. public void setUpdated(String updated) {
  70. this.updated = updated;
  71. }
  72.  
  73.  
  74. public String getDiggs() {
  75. return diggs;
  76. }
  77.  
  78. public void setDiggs(String diggs) {
  79. this.diggs = diggs;
  80. }
  81.  
  82. public String getViews() {
  83. return views;
  84. }
  85.  
  86. public void setViews(String views) {
  87. this.views = views;
  88. }
  89.  
  90. public String getComments() {
  91. return comments;
  92. }
  93.  
  94. public void setComments(String comments) {
  95. this.comments = comments;
  96. }
  97.  
  98. public String getTopicIcon() {
  99. return topicIcon;
  100. }
  101.  
  102. public void setTopicIcon(String topicIcon) {
  103. this.topicIcon = topicIcon;
  104. }
  105.  
  106. public String getSourceName() {
  107. return sourceName;
  108. }
  109.  
  110. public void setSourceName(String sourceName) {
  111. this.sourceName = sourceName;
  112. }
  113.  
  114. public String getLink_href() {
  115. return link_href;
  116. }
  117.  
  118. public void setLink_href(String link_href) {
  119. this.link_href = link_href;
  120. }
  121.  
  122. @Override
  123. public String toString() {
  124. return "BKYNews{" +
  125. "id='" + id + '\'' +
  126. ",title='" + title + '\'' +
  127. ",summary='" + summary + '\'' +
  128. ",published='" + published + '\'' +
  129. ",updated='" + updated + '\'' +
  130. ",link='" + link_href + '\'' +
  131. ",diggs='" + diggs + '\'' +
  132. ",views='" + views + '\'' +
  133. ",comments='" + comments + '\'' +
  134. ",topicIcon='" + topicIcon + '\'' +
  135. ",sourceName='" + sourceName + '\'' +
  136. '}';
  137. }
  138. }

4. 给出的实体类的规则是:

  1. * 属性名与要解析的xml标签名一致
  2. * 如果要解析标签中的属性值,则命名规则为:标签名+下划线+ 属性
  3. > 如上面给出的实体类中一个属性名叫`link_href`,即是xmllink标签中的href属性
  4.  
  5. * 暂只支持标签2`(要解析的数据有两层,而不是要解析的数据在整个xml数据中有多少层)`嵌套,不支持标签多层嵌套,(如entry标签中有author,author标签中有name),稍后我会尝试写一个,如有大神解救,甚是欢喜
  6. * 实体类名称与要解析的标签名称不一致没有关系,因为需要将要解析的根标签传入,如,我们要解析的数据是entry标签中的内容,所以要传入entry

5. 给出SAX最最重要的解析过程,这个是个工具类中的内容,可直接拷贝,无须修改

  1. /** * @author 郑明亮 * @Email zhengmingliang911@gmail.com * @Time 2017年4月9日 下午2:58:36 * @Description <p>用于解析xml为实体类的处理器 </P> * @version 1.0 */
  2. class XMlHandler<T> extends DefaultHandler{
  3. String rootElemntName;
  4. Map<String,String> dataMap;
  5. StringBuilder stringBuilder;
  6. List<T> dataList;
  7. T data;
  8. Class<T> clz;
  9. private Map<String,Class> attrs;
  10.  
  11. /** * @author 郑明亮 * @Email zhengmingliang911@gmail.com * @Time 2017年4月9日 下午3:13:43 * @Description <p> 当前标签名称 </P> * @version 1.0 */
  12. private String currentTag;
  13. /** * 要解析的单个实体的根元素名称 * @param rootElemntName * @throws IllegalAccessException * @throws InstantiationException */
  14. XMlHandler(String rootElemntName,Class<T> clz,Map<String,Class> attrsMap) {
  15. this.rootElemntName = rootElemntName;
  16. this.clz = clz;
  17. this.attrs = attrsMap;
  18. }
  19.  
  20. @Override
  21. public void startDocument() throws SAXException {
  22. super.startDocument();
  23. dataMap = new HashMap<String,String>();
  24. stringBuilder = new StringBuilder();
  25. dataList = new ArrayList<>();
  26. }
  27.  
  28. @Override
  29. public void startElement(String uri,String localName,String qName,Attributes attributes) throws SAXException {
  30. // TODO Auto-generated method stub
  31. super.startElement(uri,localName,qName,attributes);
  32. //赋值给当前标签名称
  33. currentTag = qName;
  34. if (rootElemntName.equals(qName)) {
  35. try {
  36. data = clz.newInstance();
  37.  
  38. } catch (InstantiationException e) {
  39. // TODO Auto-generated catch block
  40. e.printStackTrace();
  41. } catch (IllegalAccessException e) {
  42. // TODO Auto-generated catch block
  43. e.printStackTrace();
  44. }
  45.  
  46. }
  47. //每次对一个标签解析前,都先置为空
  48. stringBuilder.setLength(0);
  49. //如果某个标签中有属性,则将其保存到map中,保存规则:key = “标签名称_属性名称” value = “属性值”
  50. if(attributes != null && dataMap != null){
  51. for (int i = 0; i < attributes.getLength(); i++) {
  52. dataMap.put(qName+"_"+attributes.getQName(i),attributes.getValue(i));
  53. }
  54.  
  55. }
  56.  
  57.  
  58. }
  59.  
  60. @Override
  61. public void characters(char[] ch,int start,int len) throws SAXException {
  62. // TODO Auto-generated method stub
  63. super.characters(ch,start,len);
  64. stringBuilder.append(ch,len);
  65. Field[] fields = clz.getDeclaredFields();
  66. try {
  67. if (StringUtils.isNotEmpty(currentTag) && data != null) {
  68. for (Field field : fields) {
  69. String name = field.getName();
  70. if (currentTag.equals(name)) {
  71. name = name.substring(0,1).toUpperCase() + name.substring(1);
  72.  
  73. Method method = data.getClass().getMethod("set"+name,field.getType());
  74. method.invoke(data,stringBuilder.toString());
  75. }
  76. }
  77. }
  78.  
  79. } catch (NoSuchMethodException e) {
  80. // TODO Auto-generated catch block
  81. e.printStackTrace();
  82. } catch (SecurityException e) {
  83. // TODO Auto-generated catch block
  84. e.printStackTrace();
  85. } catch (IllegalAccessException e) {
  86. // TODO Auto-generated catch block
  87. e.printStackTrace();
  88. } catch (IllegalArgumentException e) {
  89. // TODO Auto-generated catch block
  90. e.printStackTrace();
  91. } catch (InvocationTargetException e) {
  92. // TODO Auto-generated catch block
  93. e.printStackTrace();
  94. }
  95.  
  96. }
  97.  
  98. @Override
  99. public void endElement(String uri,String qName) throws SAXException {
  100. super.endElement(uri,qName);
  101. if (rootElemntName.equals(qName)) {
  102. try {
  103. if (attrs != null) {
  104.  
  105. for (String attrName : attrs.keySet()) {
  106. String methodName = "set"+attrName.substring(0,1).toUpperCase()+attrName.substring(1);
  107. Method method = data.getClass().getMethod(methodName,attrs.get(attrName));
  108. method.invoke(data,dataMap.get(attrName));
  109. }
  110. }
  111. } catch (NoSuchMethodException e) {
  112. // TODO Auto-generated catch block
  113. e.printStackTrace();
  114. } catch (SecurityException e) {
  115. // TODO Auto-generated catch block
  116. e.printStackTrace();
  117. } catch (IllegalAccessException e) {
  118. // TODO Auto-generated catch block
  119. e.printStackTrace();
  120. } catch (IllegalArgumentException e) {
  121. // TODO Auto-generated catch block
  122. e.printStackTrace();
  123. } catch (InvocationTargetException e) {
  124. // TODO Auto-generated catch block
  125. e.printStackTrace();
  126. }
  127.  
  128. dataList.add(data);
  129. }
  130.  
  131. }
  132.  
  133. @Override
  134. public void endDocument() throws SAXException {
  135. System.out.println("解析结束:"+dataList);
  136. super.endDocument();
  137. }
  138.  
  139.  
  140. public List<T> getDataList(){
  141. return dataList;
  142. }
  143.  
  144. public T getData(){
  145. return data;
  146. }
  147. }

6. 工具类:主要看工具类的最后一个方法:parseXml2Bean,前面方法均是JAXB对xml与对象转换的方法

  1. import java.io.ByteArrayInputStream;
  2. import java.io.FileWriter;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.io.StringReader;
  6. import java.io.StringWriter;
  7. import java.lang.reflect.Field;
  8. import java.lang.reflect.InvocationTargetException;
  9. import java.lang.reflect.Method;
  10. import java.util.ArrayList;
  11. import java.util.HashMap;
  12. import java.util.List;
  13. import java.util.Map;
  14.  
  15. import javax.xml.bind.JAXBContext;
  16. import javax.xml.bind.JAXBException;
  17. import javax.xml.bind.Marshaller;
  18. import javax.xml.bind.Unmarshaller;
  19. import javax.xml.parsers.ParserConfigurationException;
  20. import javax.xml.parsers.SAXParser;
  21. import javax.xml.parsers.SAXParserFactory;
  22.  
  23. import org.junit.Test;
  24. import org.xml.sax.Attributes;
  25. import org.xml.sax.SAXException;
  26. import org.xml.sax.helpers.DefaultHandler;
  27.  
  28. import top.wys.developerclub.model.SysUser;
  29. import top.wys.developerclub.test.BKYAticle;
  30.  
  31. public class XmlUtils {
  32.  
  33.  
  34. /** * @author 郑明亮 * @Time 2017年4月1日19:04:50 * @Description <p> * 将实体类直接转换成xml * </p> * @param t * 要转换成xml的对象 * @return xml字符串 */
  35. public static <T> String createXmlFromBean(T t) {
  36. String xml = "";
  37. if (t == null) {
  38. return xml;
  39. } else {
  40. try (StringWriter write = new StringWriter()) {
  41. JAXBContext context = JAXBContext.newInstance(t.getClass());
  42. Marshaller marshaller = context.createMarshaller();
  43. marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true);
  44. marshaller.marshal(t,write);
  45. xml = write.toString();
  46. } catch (JAXBException e) {
  47. // TODO Auto-generated catch block
  48. e.printStackTrace();
  49. } catch (IOException e1) {
  50. // TODO Auto-generated catch block
  51. e1.printStackTrace();
  52. }
  53. return xml;
  54. }
  55. }
  56. /** * @author 郑明亮 * @Time 2017年4月1日19:04:50 * @Description <p> * 将实体类直接转换成xml,要转换的类必需加油@XmlRootElement注解 * </p> * @param t * 要转换成xml的对象 * @param format 是否格式化输出,{@code true}格式化输出,{@code false} 不进行格式化 * @return xml字符串 */
  57. public static <T> String createXmlFromBean(T t,boolean format) {
  58. String xml = "";
  59. if (t == null) {
  60. return xml;
  61. } else {
  62. try (StringWriter write = new StringWriter()) {
  63. // 利用jdk中自带的转换类实现
  64. JAXBContext context = JAXBContext.newInstance(t.getClass());
  65. Marshaller marshaller = context.createMarshaller();
  66. // 格式化xml输出的格式
  67. marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,format);
  68. // 将对象转换成xml写入到StringWriter中
  69. marshaller.marshal(t,write);
  70. xml = write.toString();
  71. } catch (JAXBException e) {
  72. // TODO Auto-generated catch block
  73. e.printStackTrace();
  74. } catch (IOException e1) {
  75. // TODO Auto-generated catch block
  76. e1.printStackTrace();
  77. }
  78. return xml;
  79. }
  80. }
  81. /** * @author 郑明亮 * @Time 2017年4月1日19:04:50 * @Description <p> * 将实体类直接转换成xml,要转换的类必需加油@XmlRootElement注解 * </p> * @param t 要转换成xml的对象 * * @param filePath 要保存到的文件路径 * @return xml字符串 */
  82. public static <T> void createXmlToFile(T t,String filePath) {
  83. try (FileWriter write = new FileWriter(filePath)) {
  84. // 利用jdk中自带的转换类实现
  85. JAXBContext context = JAXBContext.newInstance(t.getClass());
  86. Marshaller marshaller = context.createMarshaller();
  87. // 格式化xml输出的格式
  88. marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true);
  89. // 将对象转换成xml写入到StringWriter中
  90. marshaller.marshal(t,write);
  91. } catch (JAXBException e) {
  92. // TODO Auto-generated catch block
  93. e.printStackTrace();
  94. } catch (IOException e1) {
  95. // TODO Auto-generated catch block
  96. e1.printStackTrace();
  97. }
  98. }
  99.  
  100. @SuppressWarnings("unchecked")
  101. public static <T> T getBeanFromXml(String xml,Class<T> clz) {
  102. T t = null;
  103. try (StringReader reader = new StringReader(xml);) {
  104. JAXBContext context = JAXBContext.newInstance(clz);
  105. Unmarshaller unmarshal = context.createUnmarshaller();
  106. t = (T) unmarshal.unmarshal(reader);
  107. } catch (JAXBException e) {
  108. // TODO Auto-generated catch block
  109. e.printStackTrace();
  110. }
  111. return t;
  112. }
  113.  
  114. @SuppressWarnings("unchecked")
  115. public static <T> T getBeanFromXml(InputStream is,Class<T> clz) {
  116. T t = null;
  117. try{
  118. JAXBContext context = JAXBContext.newInstance(SysUser.class);
  119. Unmarshaller unmarshal = context.createUnmarshaller();
  120. t = (T) unmarshal.unmarshal(is);
  121. } catch (JAXBException e) {
  122. // TODO Auto-generated catch block
  123. e.printStackTrace();
  124. }
  125. return t;
  126. }
  127. /** * @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 */
  128. public <T> List<T> parseXml2Bean(String xml,String rootElemntName,Class> attrs){
  129.  
  130. XMlHandler<T> handler = null;
  131. try(ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes());) {
  132. handler = new XMlHandler<>(rootElemntName,clz,attrs);
  133. SAXParserFactory factory = SAXParserFactory.newInstance();
  134. SAXParser parser = factory.newSAXParser();
  135. parser.parse(stream,handler);
  136. System.out.println(handler.getDataList());
  137. } catch (ParserConfigurationException e) {
  138. // TODO Auto-generated catch block
  139. e.printStackTrace();
  140. } catch (SAXException e) {
  141. // TODO Auto-generated catch block
  142. e.printStackTrace();
  143. } catch (IOException e) {
  144. // TODO Auto-generated catch block
  145. e.printStackTrace();
  146. }
  147. return handler.getDataList();
  148. }
  149. }

7. 测试类:

  1. @Test
  2. public void testXML2Model1() throws IOException {
  3. String xml = HttpUtils.getHttpData("http://wcf.open.cnblogs.com/news/hot/10");
  4. Map<String,Class> map = new HashMap<>();
  5. map.put("link_href",String.class);
  6. List<BKYAticle> list = parseXml2Bean(xml,"entry",BKYAticle.class,map);
  7.  
  8. }

8. 效果


点次查看完整大图(一定要找到中间那条线,然后在上面放大查看哦)

文章首次发布于个人博客:吾勇士的博客点击查看原文

猜你在找的XML相关文章