Anroid开发之Xml的解析
Anroid开发之Xml的解析
YH_猿员猿 发表于2年前
Anroid开发之Xml的解析
  • 发表于 2年前
  • 阅读 2
  • 收藏 0
  • 点赞 0
  • 评论 0

【腾讯云】买域名送云解析+SSL证书+建站!>>>   

    在前面的一个博客中我们了解了json数据的解析,现在我们来实现对xml格式的数据的解析,xml是一种可扩展标记语言,是由标签组成的。方便我们读取数据。
    xml的解析有三种方式,DOM解析,SAX解析,PULL解析。

  1. DOM解析XML文件时,会将XML文件所有内容都读取到内存中,然后再遍历XML文件树,检索需要的数据。DOM解析XML有很大的缺陷,对内存的消耗比较大,影响系统的性能,所以我这里没有详细讲DOM解析。
  2. SAX解析是一种占用内存少,且解析速度快的解析器,它采用事件驱动,它不需要解析整个文档,而是按照顺序,检索XML文件是否符合XML的语法,当符合时会触发相应的回调函数。这些函数是在ContentHander中,我们可以重写这些方法来实现我们对数据的解析。
  3. Pull解析是一种可以解析部分XML文件的解析方式,当我们只需要XML文件中的一部分数据时,用这种方法是非常好的。Pull解析和SAX解析类似,SAX解析器工作方式是自动的将事件推入注册的事件处理器进行处理,因此你不能控制事件的处理主动结束,而Pull解析的工作方式为允许你的应用程序代码主动从解析器获取事件,因此我们可以在获取了我们需要的数据时,主动结束解析。

SAX解析
SAX解析的主要是重写ContentHander中回调方法,在其中主要的是:

  1. startDocument:当遇到文档时候触发事件。
  2. startElement:当遇到开始标签时触发。
  3. endElement:当遇到结束标签时触发。
  4. characters:当遇到xml内容时触发。
    这里写图片描述

上面是我自己建的一个xml文件,命名为students.xml,并将它放在assets文件夹下。

1.我首先定义了一个student类对象,用来存放解析出来的xml数据

public class Student {

    private Long Id;
    private String Name;
    private String Speciality;
    private Long QQ;
    public Student() {
    }
    //省略了get和set方法
}

2.实例化一个SAX工厂,并获取SAX解析器,读取xml文件,创建回调函数

protected List<Student> parserXML() {
        //实例化一个SAX解析工厂
        SAXParserFactory factory = SAXParserFactory.newInstance();
        List<Student> studnets = null;
        //获取XML解析器
        try {
            XMLReader reader = factory.newSAXParser().getXMLReader();
            studnets = new ArrayList<Student>();
            //设置回调函数,这里是我自定义的回调函数
            reader.setContentHandler(new StudentHander(studnets));
            //读取Assets下的student.xml文件
            reader.parse(new InputSource(SAXXMLActivity.this.
                    getResources().getAssets().open("students.xml")));
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return studnets;

3.自定义StudentHander回调函数,继承自DefaultHander;在回调方法里解析出自己需要的数据。其中preTAG用来存储当前节点名称,在endElement中要记得滞空。

public class StudentHander extends DefaultHandler {
    private String preTAG;
    private List<Student> ListStudent;
    private Student stu;
    public StudentHander() {
    }

    public StudentHander(List<Student> listStudent) {
        super();
        ListStudent = listStudent;
    }


    //开始解析文档
    @Override
    public void startDocument() throws SAXException {
        Log.i("----->", "开始解析文档!");
        super.startDocument();
    }
    //开始解析文档元素
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        Log.i("localName----->", localName);
        preTAG = localName;
        if ("student".equals(localName)) {
            stu = new Student();
            //将ID保存在stu中
            stu.setId(Long.parseLong(attributes.getValue(0)));
            for (int i = 0; i < attributes.getLength(); i++) {
                Log.i("attribute----->", String.valueOf(stu.getId()));
            }
        }
        //这句话记得要执行
        super.startElement(uri, localName, qName, attributes);
    }
    @Override
    public void endDocument() throws SAXException {
        Log.i("----->", "文档结束");
        super.endDocument();

    }
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        preTAG = "";
        if ("student".equals(localName)) {
            ListStudent.add(stu);
            Log.i("----->", "一个元素解析完成!");
        }
        super.endElement(uri, localName, qName);
    }
    //解析节点文本内容
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        String str;
        //找出元素中的name节点
        if ("name".equals(preTAG)) {
            str = new String(ch,start,length);
            stu.setName(str);
            Log.i("name=", str);
        }else if("speciality".equals(preTAG)){
            str = new String(ch,start,length);
            stu.setSpeciality(str);
            Log.i("speciality=", str);
        }else if("qq".equals(preTAG)){
            str = new String(ch,start,length);
            stu.setQQ(Long.parseLong(str));
            Log.i("qq=", str);
        }

        super.characters(ch, start, length);
    }
    public void setListStudent(List<Student> listStudent) {
        ListStudent = listStudent;
    }
    public List<Student> getListStudent() {
        return ListStudent;
    }
}

这样我们就解析出xml的整个数据了,并在回调函数中将数据保存到了数组对象students中,我们查看一下打印出的log,分析一下SAX的解析过程。
这里写图片描述

我们可以看出这种按顺序解析XML文件标签的方式,完全符合回调函数中的说明。清晰明了。


Pull解析
pull解析和我们上面的SAX解析方法有些类似,Pull解析也提供了类似SAX的事件:

  1. START_DOCUMENT:开始文档;
  2. START_TAG:开始元素;
  3. TEXT:遇到元素内容;
  4. END_TAG:结束元素
  5. END_DOCUMENT:结束文档

Android系统提供了Pull解析的包org.xmlpull.v1,里面有PUll解析工厂类XMLPullParserFactory,和PULL解析器XmlParser。我们实例化工厂类,并获取xml解析器,接着XmlParser实例就可以调用getEventType()和next()等方法主动提取事件,并根据提取的事件做数据的处理。

PULL解析器有两种获取方法:

  1. 通过工厂类XMLPullParserFactory
  2. 通过Android实用工具类
protected List<Student> parserXML() {
        //初始化一个List<student>变量,用于存放student成员
        List<Student> studnets = null;
        //初始化student对象,用于存储每一个节点的信息
        Student stu = null;

        try {
            //打开资源文件
            InputStream inputStream = PullXMLActivity.this.getResources()
                    .getAssets().open("students.xml");
            //创建XmlParser有两种方法
            //方法一:使用工厂类XmlPullParserFactory
            XmlPullParserFactory pullFactory = XmlPullParserFactory.newInstance();
            XmlPullParser xmlPullParser = pullFactory.newPullParser();
            /* * //方法二:使用Android提供的实用工具类android.util.Xml //XmlPullParser xmlPullParser = Xml.newPullParser(); //设置输入字节流为UTF-8编码 * */
            xmlPullParser.setInput(inputStream, "utf-8");
            //取得事件类型,用于开始时的判断
            int eventType = xmlPullParser.getEventType();
            //循环遍历整个文件直到结束
            while(eventType != XmlPullParser.END_DOCUMENT){
                /* * 输出log显示事件类型 * START_DOCUMENT:0 * */
                Log.i("--->event", eventType+" ");
                //用于存储节点名称
                String localName = "";
                switch (eventType) {
                case XmlPullParser.START_DOCUMENT:
                    //碰到文档开头则实例化students变量,并输出log
                    studnets = new ArrayList<Student>();
                    Log.e("Pull---->", "start document");
                    break;
                case XmlPullParser.START_TAG:
                    localName = xmlPullParser.getName();
                    if ("student".equals(xmlPullParser.getName())) {
                        stu = new Student();
                        //将ID保存到stu中
                        stu.setId(Long.parseLong(xmlPullParser.getAttributeValue(0)));
                        Log.e("Pull----->", stu.getId()+"");
                    }else if(stu != null){
                        //声明一个变量用于存储节点文本
                        String currentData = null;
                        if ("name".equals(xmlPullParser.getName())) {
                            /* * 注意这里nextText()的使用:当前事件为START-TAG * 如果接下来是文本,就会返回当前的文本内容;如果下一个事件是End_TAG * 就会返回空字符串;否则抛出一个yichang */
                            currentData = xmlPullParser.nextText();
                            //存储name的信息
                            stu.setName(currentData);

                        }else if("speciality".equals(xmlPullParser.getName())){
                            currentData = xmlPullParser.nextText();
                            //存储专业信息
                            stu.setSpeciality(currentData);
                        }else if("qq".equals(xmlPullParser.getName())){
                            currentData = xmlPullParser.nextText();
                            //存储专业信息
                            stu.setQQ(Long.parseLong(currentData));
                        }
                    }
                    break;
                case XmlPullParser.END_TAG:
                    localName = xmlPullParser.getName();
                    Log.e("Pull----->", localName);
                    if ("student".equals(localName) && stu != null) {
                        studnets.add(stu);
                        stu = null;
                    }
                    break;
                default:
                    break;
                }
                //解析下一事件
                eventType = xmlPullParser.next();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (XmlPullParserException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return studnets;
    }

这里也打印出log贴出来并做出了分析
这里写图片描述

总结:
SAX和PULL解析器虽然在代码上可能写的有些复杂,但是它们的解析效率,和思维逻辑的清晰都是非常好的,值得我们在软件开发中使用。

  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 0
博文 12
码字总数 0
×
YH_猿员猿
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: