自定义xml解析框架

标签:

我们在工作中,经常会从服务器获取数据并进行解析,服务器返回的数据有两种:json和xml。json我们可以用gson或者fastjson等优秀的开源框架去进行解析,省去不少麻烦,通常我们只需要把bean设计出来,然后调用一句话就可以解析成功了。而解析xml数据,我们得去一个标签一个标签的去比较,修改起来也非常麻烦,比如我们现在不需要某一个标签上的数据了,我们还得去把代码中那一行给删除,如果我们的需求又变了,现在又需要加上某个标签的数据,那只能再到代码中把相应代码加上,这样非常麻烦,这篇文章我们将介绍如何设计我们自己的xml解析框架,做到可以和gson一样,只需要关心bean的结构,调用一句代码就可以解析成功。

首先我们先来分析一下服务器返回回来的xml数据的格式,然后进行设计。我的数据是从聚合数据上面得到的。

<?xml version="1.0" encoding="utf-8"?>
<root>
    <reason>查询成功</reason>
    <result>
        <title>NBA2014-2015赛季_火箭队赛程赛果</title>
        <list>
            <item>
                <c1></c1>
                <c2>10-25</c2>
                <c3>08:00</c3>
                <c4R>87-96</c4R>
                <c4T1>马刺</c4T1>
                <c4T1URL>http://nba.sports.sina.com.cn/team.php?id=24</c4T1URL>
                <c4T2>火箭</c4T2>
                <c4T2URL>http://nba.sports.sina.com.cn/team.php?id=10</c4T2URL>
                <c51></c51>
                <c52>视频集锦</c52>
                <c52Link>http://video.sina.com.cn/z/sports/k/nba/141025sashou/</c52Link>
                <c53>数据统计</c53>
                <c53Link>http://nba.sports.sina.com.cn/look_scores.php?id=2014102410</c53Link>
                <c54>新闻报道</c54>
                <c54Link>http://sports.sina.com.cn/nba/2014-10-25/10267382993.shtml</c54Link>
            </item>
            <item>
                <c1></c1>
                <c2>10-29</c2>
                <c3>10:30</c3>
                <c4R>108-90</c4R>
                <c4T1>火箭</c4T1>
                <c4T1URL>http://nba.sports.sina.com.cn/team.php?id=10</c4T1URL>
                <c4T2>湖人</c4T2>
                <c4T2URL>http://nba.sports.sina.com.cn/team.php?id=13</c4T2URL>
                <c51></c51>
                <c52>视频集锦</c52>
                <c52Link>http://video.sina.com.cn/z/sports/k/nba/141029houlal/</c52Link>
                <c53>数据统计</c53>
                <c53Link>http://nba.sports.sina.com.cn/look_scores.php?id=2014102813</c53Link>
                <c54>新闻报道</c54>
                <c54Link>http://sports.sina.com.cn/nba/2014-10-29/13367388047.shtml</c54Link>
            </item>
            <item>
                <c1></c1>
                <c2>10-30</c2>
                <c3>09:00</c3>
                <c4R>104-93</c4R>
                <c4T1>火箭</c4T1>
                <c4T1URL>http://nba.sports.sina.com.cn/team.php?id=10</c4T1URL>
                <c4T2>爵士</c4T2>
                <c4T2URL>http://nba.sports.sina.com.cn/team.php?id=26</c4T2URL>
                <c51></c51>
                <c52>视频集锦</c52>
                <c52Link>http://video.sina.com.cn/z/sports/k/nba/141030houuta/</c52Link>
                <c53>数据统计</c53>
                <c53Link>http://nba.sports.sina.com.cn/look_scores.php?id=2014102926</c53Link>
                <c54>新闻报道</c54>
                <c54Link>http://sports.sina.com.cn/nba/2014-10-30/11267389310.shtml</c54Link>
            </item>
            <item>
                <c1></c1>
                <c2>11-02</c2>
                <c3>08:00</c3>
                <c4R>VS</c4R>
                <c4T1>凯尔特人</c4T1>
                <c4T1URL>http://nba.sports.sina.com.cn/team.php?id=2</c4T1URL>
                <c4T2>火箭</c4T2>
                <c4T2URL>http://nba.sports.sina.com.cn/team.php?id=10</c4T2URL>
                <c51></c51>
                <c52>文字直播</c52>
                <c52Link>http://sports.sina.com.cn/nba/live.html?id=2014110110</c52Link>
                <c53></c53>
                <c53Link></c53Link>
                <c54></c54>
                <c54Link></c54Link>
            </item>
            <item>
                <c1></c1>
                <c2>11-04</c2>
                <c3>08:00</c3>
                <c4R>VS</c4R>
                <c4T1>火箭</c4T1>
                <c4T1URL>http://nba.sports.sina.com.cn/team.php?id=10</c4T1URL>
                <c4T2>76人</c4T2>
                <c4T2URL>http://nba.sports.sina.com.cn/team.php?id=20</c4T2URL>
                <c51></c51>
                <c52>文字直播</c52>
                <c52Link>http://sports.sina.com.cn/nba/live.html?id=2014110320</c52Link>
                <c53></c53>
                <c53Link></c53Link>
                <c54></c54>
                <c54Link></c54Link>
            </item>
            <item>
                <c1></c1>
                <c2>11-05</c2>
                <c3>08:30</c3>
                <c4R>VS</c4R>
                <c4T1>火箭</c4T1>
                <c4T1URL>http://nba.sports.sina.com.cn/team.php?id=10</c4T1URL>
                <c4T2>热火</c4T2>
                <c4T2URL>http://nba.sports.sina.com.cn/team.php?id=14</c4T2URL>
                <c51></c51>
                <c52>文字直播</c52>
                <c52Link>http://sports.sina.com.cn/nba/live.html?id=2014110414</c52Link>
                <c53></c53>
                <c53Link></c53Link>
                <c54></c54>
                <c54Link></c54Link>
            </item>
        </list>
        <more1>
            <link>http://nba.sports.sina.com.cn/team_match.php?id=10</link>
            <txt>火箭队完整赛程</txt>
        </more1>
        <more2>
            <link>http://nba.sports.sina.com.cn/showtv.php</link>
            <txt>电视转播表</txt>
        </more2>
    </result>
    <error_code>0</error_code>
</root>
1.我们使用xml的pull解析方法,解析的过程是从第一个开始标签顺序往下直到最后一个结束标签。先确定我们需要哪些标签上的数据。reason和title上面有数据,我们想要得到。然后就是一堆item,每个item里面都有一些数据,我们想要得到。
2.分析结构:item一看就知道是要存储在一个list里面的,每个item里面的标签也都是一样的,只是数据不一样而已,所以我们要设计一个ListBean,用来保存item里面的数据,然后放到一个ArrayList里面。这样可以将所有的item都保存起来。
public class ListBean {
    public String c1;
    public String c2;
    public String c3;
    public String c4R;
    public String c4T1;
    public String c4T1URL;
    public String c4T2;
    public String c4T2URL;
    public String c51;
    public String c52;
    public String c52Link;
    public String c53;
    public String c53Link;
    public String c54;
    public String c54Link;
    @Override
    public String toString() {
        return "ListBean [c1=" + c1 + ", c2=" + c2 + ", c3=" + c3 + ", c4R="
                + c4R + ", c4T1=" + c4T1 + ", c4T1URL=" + c4T1URL + ", c4T2="
                + c4T2 + ", c4T2URL=" + c4T2URL + ", c51=" + c51 + ", c52="
                + c52 + ", c52Link=" + c52Link + ", c53=" + c53 + ", c53Link="
                + c53Link + ", c54=" + c54 + ", c54Link=" + c54Link + "]";
    }
    
}

再看reason和title,这两个标签也应该保存在一个bean中,我们在设计一个Bean

public class Bean {
    public String reason;
    public String title;
    @Override
    public String toString() {
        return "Bean [reason=" + reason + ", title=" + title + "]";
    }
    
}
整个xml数据的结构为一个bean和一个list的结构:bean:reason和title    list:所有的item
3.我们再设计一个最终结果的实体类,一个bean和一个list
public class ResultBeanAndList<T> {
    public Object bean;
    
    public ArrayList<T> list;
    
    
    public Object getBean() {
        return bean;
    }
    public void setBean(Object bean) {
        this.bean = bean;
    }
    public ArrayList<T> getList() {
        return list;
    }
    public void setList(ArrayList<T> list) {
        this.list = list;
    }
    @Override
    public String toString() {
        return "ResultBeanAndList [bean=" + bean + ", list=" + list + "]";
    }
    
    
}
其中的T是一个泛型,里面传入的是item的实体类,即这个例子中的ListBean
4.在解析的过程中是从第一个标签顺序向后解析的,我们必须得让parser知道:什么时候是外层的Bean,什么时候是内层的ListBean,什么时候将ListBean加入到list中。其实很简单,当parser解析到<root>的时候,我们就让Bean实例化出来,当解析到</root>的时候,就将Bean保存到ResultBeanAndList中。当parser解析到<item>的时候,我们将ListBean实例化出来,当解析到</list>的时候,将ListBean加入到ResultBeanAndList中。
public class XmlUtils {
    /**
     * 解析一个bean和一个list的xml文件结构的方法
     * @param parser 解析者
     * @param listRoot 内层ListBean需要实例化对象的一个标识
     * @param listClazz ListBean.class
     * @param beanRoot 外层Bean需要实例化对象的一个标识
     * @param beanClazz Bean.class
     * @return 一个bean和一个list的结果
     * @throws Exception
     */
    public static <T,T1> ResultBeanAndList<T> getBeanByParseXml(XmlPullParser parser , String listRoot , Class<T> listClazz ,String beanRoot , Class<T1> beanClazz) throws Exception{
        //最后结果
        ResultBeanAndList<T> result = null;
        //list  存放一堆item
        ArrayList<T> list = null;
        //内层ListBean
        T t = null;
        //外层Bean
        T1 bean = null;
        //一个计数器
        int count = 0 ;
        try {
            //获得当前标签类型
            int eventType = parser.getEventType();
            //如果不是xml文件结束标签,则一个一个向下解析
            while(eventType != XmlPullParser.END_DOCUMENT){
                switch (eventType) {
                //如果是xml文件开始标签,则初始化一些数据
                case XmlPullParser.START_DOCUMENT:
                    //最后的结果
                    result = new ResultBeanAndList<T>();
                    //list
                    list = new ArrayList<T>();
                    //将list加入到result中,当前list是空的,等后面加入了数据后,就不是空了
                    result.setList(list);
                    break;
                //开始标签
                case XmlPullParser.START_TAG:
                    //获得标签的名字
                    String tagName = parser.getName();
                    //如果内层的ListBean已经实例化出来的话
                    if (t != null) {
                        try {
                            //判断当前标签在没在ListBean的属性中
                            Field field = listClazz.getField(tagName);
                            //如果ListBean中有当前标签
                            if (field != null) {
                                //计数器+1
                                count++;
                                //将取出来的值赋给ListBean中对应的属性
                                field.set(t, parser.nextText());
                            }
                        } catch (Exception e) {
                            //如果ListBean中没有当前标签,则会直接跳到这里,什么都不执行,然后再继续往下走
                            
                        }
                    //如果外层的Bean已经实例化出来的话
                    }else if (bean != null) {
                        try {
                            //判断当前标签在没在Bean的属性中
                            Field field = beanClazz.getField(tagName);
                            //如果Bean中有当前标签
                            if (field != null) {
                                //计数器+1
                                count++;
                                //将取出来的值赋给Bean中对应的属性
                                field.set(bean, parser.nextText());
                            }
                        } catch (Exception e) {
                            //如果Bean中没有当前标签,则会直接跳到这里,什么都不执行,然后再继续往下走
                        }
                    }
                    //如果当前标签为我们传入的内层根标签,说明ListBean需要实例化出来了
                    if (tagName.equals(listRoot)) {
                        //将ListBean实例化出来
                        t = listClazz.newInstance();
                    }
                    //如果当前标签为我们传入的内层根标签,说明Bean需要实例化出来了
                    if (tagName.equals(beanRoot)) {
                        //将Bean实例化出来
                        bean = beanClazz.newInstance();
                    }
                    break;
                //结束标签
                case XmlPullParser.END_TAG:
                    //如果当前标签为</item>
                    if (listRoot.equalsIgnoreCase(parser.getName())) {
                        //如果ListBean不为空
                        if (t != null) {
                            //保存到list中,同时也保存到了result中,因为list已经是保存在result中了,
                            //只不过刚才没有值,现在有值了
                            list.add(t);
                            //并且把ListBean置空,因为后续还有好多个item
                            t = null;
                        }
                    //如果当前标签为</root>
                    }else if (beanRoot.equalsIgnoreCase(parser.getName())) {
                        //将Bean保存到result中
                        result.setBean(bean);
                    }
                    break;
                }
                //移动到下一个标签
                eventType = parser.next();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //如果计数器为0说明没有解析到任何数据
        if (count == 0) {
            //将result置空就可以了
            result = null;
        }
        //将result返回
        return result;
        
    }
}

5.我们使用基于Volley的自定义Request获取xml数据,关于如何自定义Request请参见Android网络框架-Volley(五) 使用Volley发送自定义Request

public class MainActivity extends Activity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String url = "http://op.juhe.cn/onebox/basketball/team";
        //我们使用基于Volley自定义的XMLRequest获取xml数据,post方法
        XMLRequest xmlRequest = new XMLRequest(Method.POST, url, new Listener<XmlPullParser>() {


            @Override
            public void onResponse(XmlPullParser response) {
                try {
                    //我们看到第二个参数 “listRoot”我们传入的是item。第三个参数是ListBean.class
                    //第四个参数“beanRoot”我们传入的是root。第四个参数是Bean.class
                    ResultBeanAndList<ListBean> result = XmlUtils.getBeanByParseXml(response, "item", ListBean.class, "root", Bean.class);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            
        }, new Response.ErrorListener(){


            @Override
            public void onErrorResponse(VolleyError error) {
                
            }
            
        }){
            //聚合数据文档要求需要传递以下参数,大家可以到聚合数据去申请一下,就会得到一个key
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                Map<String, String> params = new HashMap<String, String>();
                params.put("key","14acc11b5dd32beb57b90181c80c76b2 ");
                params.put("team", "火箭");
                params.put("dtype", "xml");
                return params;
            }
        };
        //将request加入到requestQueue中
        AppController.getInstance().addToRequestQueue(xmlRequest);
    }
}

6.从xml获取的数据如下:

技术分享
最后结果result中的数据如下
技术分享
总结:
1.代码中的注释为了避免大家混淆,我都用的这个例子中的实体类的名字,其实我们定义了泛型,实体类可以传入任何一个类,这个根据我们实际的需要,设计我们需要的实体类就可以了。比如我们不需要title这个数据了,那我们直接将Bean中的title属性删除就可以了,如果我们在Bean中加入了一个不相关的属性,例如Public String abc;
我们也不需要担心出错,通过Field field = listClazz.getField(tagName);如果xml文件中没有<abc>这个标签的话,就会直接跳到catch里,接着会向后执行,不会出任何错误。
2.这个例子中的xml文件是一个bean和一个list的结构,如果是一个bean和两个list的结构,我们就需要再按照此思路设计一个解析方法, 如果是只有一个bean的结构那更简单了,我们在工作中,遇到一种结构,就将其设计出来加入到XmlUtil文件中。这样就形成了一款我们自己的xml解析框架,以后再有相同结构的xml文件,直接设计实体类即可。
3.自定义bean时要注意属性修饰符不要为private否则反射机制获取不到结果. 直接复用xmlUtil 和 ResultListAndBean两个类 然后自定义需要的bean 传入对应的节点即可
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值