市面上有许多API提供商,JSON和XML成为了主要的信息载体,数据解析是相当重要。

1.JSON,由字符串根据特定的格式形成的,传输和解析的速度都非常快。

2.XML,标签式文档,易于拓展,性能没JSON好,但处理大量的数据或者复杂的数据比JSON好使。


本帖用实例代码详解四种数据解析方式,两种是XML的,两种是JSON的。至于JSON和XML的写法规则,大家都懂的,没必要写了。

JSON:Android自带的JSON解析,Gson谷歌的东西。

XML:SAXPaser,XmlPullPaser,这两种性能较好,适合移动设备。


先看看数据结构和需求,需求是,找出这个school里B班的所有学生的信息。

school

     classA

                 学生信息,id=101,classmate=Ben

                 .............................................................

                 .............................................................

     classB

                .............................................................

                .............................................................

.               ............................................................

     classC

                .............................................................

                .............................................................



一、先来JSON,看看数据如下图:

{
    "school":{
        "classA":[
            {
                "id":"101",
                "classmate":"Ben"
            },
            {
                "id":"102",
                "classmate":"Bell"
            },
            {
                "id":"103",
                "classmate":"Bean"
            }
        ],
        "classB":[
            {
                "id":"201",
                "classmate":"Peter"
            },
            {
                "id":"202",
                "classmate":"Paul"
            },
            {
                "id":"203",
                "classmate":"Po"
            }
        ],
        "classC":[
            {
                "id":"301",
                "text":"Sarah"
            },
            {
                "id":"302",
                "text":"Steven"
            },
            {
                "id":"303",
                "text":"Sam"
            }
        ]
    }
}

(1)Gson

Gson有个好处,可以根据JavaBean的结构。

先根据上述数据写个结构先,GsonBean.java。

规则是JSONOject的话用类对象(除了String这个类,还可以自定义或其他的),JSONArray的话用List集合(集合里的类除了String也可以放自定义或其他的)。

第一层,school的内容是个JSONOject,用一个自定义School类定义。

第二层,school的内容这个JSONOject里面放的是三个班的信息,自定义School类放的是定义三个班的信息。

第三层,三个class放的的内容都是一个JSONArray,用List集合。

第四层,上一层的List集合里,每个同学的信息是一个JSONOject,我没把同学的信息的JavaBean写在GsonBean.java,按照上述的规律,写在另外的JavaBean文件ClassmateBean.java。


GsonBean.java

package ljy.com.jsonandxmldemo;

import java.util.List;

/**
 * Created by Administrator on 2016/4/21.
 */
public class GsonBean {
    private School school;

    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }

    public class School {
        private List<ClassmateBeen> classA;
        private List<ClassmateBeen> classB;
        private List<ClassmateBeen> classC;

        public List<ClassmateBeen> getClassA() {
            return classA;
        }

        public void setClassA(List<ClassmateBeen> classA) {
            this.classA = classA;
        }

        public List<ClassmateBeen> getClassB() {
            return classB;
        }

        public void setClassB(List<ClassmateBeen> classB) {
            this.classB = classB;
        }

        public List<ClassmateBeen> getClassC() {
            return classC;
        }

        public void setClassC(List<ClassmateBeen> classC) {
            this.classC = classC;
        }
    }
}



ClassmateBean.java


package ljy.com.jsonandxmldemo;
public class ClassmateBeen {
    private String id;
    private String classmate;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getClassmate() {
        return classmate;
    }

    public void setClassmate(String classmate) {
        this.classmate = classmate;
    }

    @Override
    public String toString() {
        return "id="+id+",classmate="+classmate;
    }
}



以上两个JavaBean是JSON解析的模型。下面是解析的主要代码,GsonRunnable.java里的代码片段。

第一步,new一个Gson对象。

第二步,new一个TypeToken<T>(){}的对象,T是泛类,传入的是解析结构模型,就是刚刚定义的GsonBean。

第三步,gson.from(参数1,参数2)方法就是解析json的。

参数1是一个Reader类型或字符串。我的json数据是InputStream流的形式,这里传入的是new一个BufferReader传进去,当然也可以传入String。

参数2是上一步的type(解析结构模型)。

最终这个方法返回一个GsonBean的类型,已经在根据我设定的解析结构模型解析完了,可以获取数据了。

第四步,返回一个GsonBean对象,看上述JavaBean结构,先获取school,然后才到classB,最后得到一个List<ClassmateBean>集合,遍历这个集合获取B班所有同学的信息。

StringBuffer sb=new StringBuffer();
        Gson gson=new Gson();
        Type type=new TypeToken<GsonBean>(){}.getType();
        GsonBean bean=gson.fromJson(new BufferedReader(new InputStreamReader(is)),type);
        List<ClassmateBeen> classB_list=bean.getSchool().getClassB();



(2)Android自带的JSON解析

Android自带的JSONOject和JSONArray,使用起来也是非常方便的。比起Gson的json解析就少了个数据模块化的过程而已。

同样的数据和需求,下面来分析下代码:

JsonRunnable.java    【流转为String】的方法。

传入的数据是流的形式,首先要有一个把流转为String的方法。这个没啥好说的,拷贝就行了。

private String Is2String(InputStream is) {
        StringBuffer buff=new StringBuffer();
        BufferedReader re=new BufferedReader(new InputStreamReader(is));
        String temp=null;
        try {
            while ((temp=re.readLine())!=null){
                buff.append(temp);
                temp=null;
            }
            return buff.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
}

JsonRunnable.java   解析的片段


第一步,因为数据最外层的是一个JSONObject,所以new一个JSONObject对象,new JSONObject(参数),参数是一个字符串。

第二步,可以根据节点get相应的东西,先get一下school的,school的内容是一个JSONObject,所以使用方法getJSONObject("school"),参数是一个节点的名称。

第三步,classB的内容是一个JSONArray,所以使用方法getJSONArray("classB")。

第四步,最终遍历得到的这个classB的内容的JSONArray,就可以获取B班同学的信息。

</pre><pre name="code" class="java">        String json=Is2String(is);
        if(json!=null){
            try {
                JSONArray arr=new JSONObject(json).getJSONObject("school").getJSONArray("classB");
                JSONObject temp;
                for(int i=0;i<arr.length();i++){
                    temp=arr.getJSONObject(i);
                    sb.append("id="+temp.getString("id")
                                +",classmate="+temp.getString("classmate")
                                +"\r\n");
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
<span >	</span>}


json的解析并不难吧,来来去去都只是JSONObject和JSONArray。



二、再来XML,看看数据,我把用了上面JSON的数据,写成XML。

也可以通过[在线互换格式的数据的网站]转过来。

数据如下图:


根标签是school,二级标签是class,三级标签是同学的信息。


这里把同学的学号写在了classmate的标签内,是这个标签的属性,标签内的姓名,就是标签的内容。


<?xml version="1.0" encoding="utf-8"?>
<school>
    <classA>
        <classmate id="101">Ben</classmate>
        <classmate id="102">Bell</classmate>
        <classmate id="103">Bean</classmate>
    </classA>
    <classB>
        <classmate id="201">Peter</classmate>
        <classmate id="202">Paul</classmate>
        <classmate id="203">Po</classmate>
    </classB>
    <classC>
        <classmate id="301">Sarah</classmate>
        <classmate id="302">Steven</classmate>
        <classmate id="303">Sam</classmate>
    </classC>
</school>



(1)SAXPaser



速度非常快的解析方式,但不能中途停止,要从头解析的最后。程序代码也做了一些优化,在解析过程如果找够了需要的数据,快速结束此次解析。




SaxHandler.java  内容看起来有点多,并不复杂,先继承DefaultHandler,然后重写的方法有那么几个:


startDocument(文档开始解析,就是读到根标签的开头),endDocument(文档结束解析,就是读到根标签的结尾),startElement(读到根标签里面的标签的开头),characters(执行完startElement,再回调此方法(开始和结束标签之间的内容),才执行endElement),endElement(读到根标签里面的标签的结尾)。




这里读取的步骤:


1.startDocument,读到根标签的时候,初始化一个用来储存同学信息的集合.


2.startElement,读取根标签内的标签的时候,做下判断:


    (1)如果读到classB的标签的话,在全局变量isClassB上做个标记,再读到下面的characters和endElement方法的时候,如果不是classB标签内的话,不执行任何操作直接return,加快解析速度。


    (2)如果标签读到已经是在classB标签内的话,再判断是否是climate标签,用全局变量isClassB_ele做标记,如果是的话,在startElement方法里,new一个Classmate的对象,上面解析json的时候用到的这个对象,可以复用,储存一个同学的学号信息,因为上述XML结构,同学的学号信息在classmate的开始标签内。然后到了characters方法,储存同一个同学的姓名,因为姓名在两个标签之间。最后到endElement方法,把储存了这名同学的所有信息的Classmate对象,加到第1步初始化的集合中。


    (3)以此类推,把所有需要的信息获取完。



整个处理器部分就写完了,解析完成了一大半了。


SaxHandler.java


package ljy.com.jsonandxmldemo;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2016/4/20.
 */
public class SaxHandler extends DefaultHandler {
    private List<ClassmateBeen> classb_list = null;
    private ClassmateBeen classb = null;
    private boolean isClassB = false;
    private boolean isClassB_ele = false;

    public List<ClassmateBeen> getData() {
        return classb_list;
    }

    public SaxHandler() {
        classb_list = new ArrayList<>();
    }

    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
//            Log.d("SAX","uri="+uri.toString()+",localname="+localName
//                    +",attributes=["+attributes.getLocalName(0)+","+attributes.getQName(0)+","+attributes.getValue(0)+"]");
        if (qName.equals("classB")) {
            isClassB = true;
        } else if (isClassB && qName.equals("classmate")) {
            classb = new ClassmateBeen();
            classb.setId(attributes.getValue(0));
            isClassB_ele = true;
        } else {
            return;
        }
    }
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        if (!isClassB) return;
        super.characters(ch, start, length);
        if (isClassB_ele)
            classb.setClassmate(new String(ch, start, length));
        isClassB_ele = false;
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if (!isClassB) return;
        super.endElement(uri, localName, qName);
        if (qName.equals("classB")) {
            isClassB = false;
            return;
        } else if (isClassB && qName.equals("classmate")) {
            classb_list.add(classb);
            classb = null;
        }
    }

    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
    }
}


上面处理器写好了,下面简单几句代码可以完事了。


1.先new一个SAXPaser的对象,使用工厂方法SAXParserFactory.newInstance().newSAXParser()。


2.再new一个刚才写好的处理器的对象,new SaxHandler()。


3.第1步的SAXPaser的对象里有paser个方法,paser(参数1,参数2),参数1是数据,参数2是解析的处理器。一句代码,处理器就立马全速运行了。


4.SAXPaser的paser方法没有返回值,注意上面处理器的代码,我写了一个getData方法返回数据的,执行完paser方法,就可以调用处理器的getData方法,获得B班所有同学的信息,这数据类型是List集合,不用说遍历就可以获得每个同学的信息了。


SAXRunnable.java的片段


try {
            SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
            SaxHandler saxHandler = new SaxHandler();
            saxParser.parse(is, saxHandler);
            StringBuffer sb = new StringBuffer();
            for (ClassmateBeen c : saxHandler.getData()) {
//                Log.d("sax",c.getId()+","+c.getName());
                sb.append(c.toString());
                sb.append("\r\n");
            }
            sb.append("paser for SAXParser\r\n");
            callback.CallbackData(sb);
//            Log.d("sax","end");
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }



(2)XmlPullPaser


这个解析方式,原理和SAXPaser大同小异的,可以随时停止的,不会想SAXPaser那样全速运行的,也没有什么专门处理解析处理器,单凭判断和循环即可。


步骤:

1.new一个XmlPullParser对象,工厂方法XmlPullParserFactory.newInstance().newPullParser()。


2.输入数据,XmlPullParser的setInput(参数1,参数2)方法,参数1是数据的流,参数2是数据编码方式(都是utf-8的了)。


3.跟上面说的解析处理器相似,XmlPullParser有几个状态,START_DOCUMENT,END_DOCUMENT,START_TAG,TEXT,END_TAG,应该知道这五个是什么了吧。


4.判断和循环方式操作的步骤:(其实跟上面的重写的解析处理器的逻辑一样的,书写代码的形式不一样而已)


     (1)先从XmlPullParser获取首个状态,再写个while循环 while (eventCode != org.xmlpull.v1.XmlPullParser.END_DOCUMENT),意思是指,到了根标签的末尾,才结束循环,意思就是解析完成。注意的是,与SAXPaser不同的是,采用while循环 代替 解析处理器,eventCode第一次循环外获取,之后都在循环结尾获取XmlPullParser的下一步状态,直到END_DOCUMENT状态才结束循环。


     (2)while循环里,switch条件语句,分别列出了START_DOCUMENT,START_TAG,END_TAG,这三种状态的操作,END_DOCUMENT这个状态确实没什么用,上面也没写,TEXT获取文本的状态为何没写呢,这里我有个更方便的写法可以省略它。


     (3)START_DOCUMENT状态,一如既往地实例化一个储存B班所有同学的信息的List集合。


意思是让XmlPullParser执行下一步,返回的肯定是TEXT状态,paser.getText()方法可以获得标签的内容,也就是姓名。这样做就不用等到结尾的时候,才执行paser.next(),少跑了一次循环。


     (5)循环也做了跟SAXPaser的优化,用return加速执行速度。最后,照旧了,跑完循环,遍历储存Classmate的List集合,获得每个同学的信息。



XmlPullParserRunnable.java

private StringBuffer paserXML(InputStream is) {
        StringBuffer sb = new StringBuffer();
        List<ClassmateBeen> classB_Member = null;
        ClassmateBeen classmate = null;
        org.xmlpull.v1.XmlPullParser paser = null;
        try {
            paser = XmlPullParserFactory.newInstance().newPullParser();
            paser.setInput(is, "utf-8");
            int eventCode = paser.getEventType();
            boolean isClassB = false;
            while (eventCode != org.xmlpull.v1.XmlPullParser.END_DOCUMENT) {
                switch (eventCode) {
                    case org.xmlpull.v1.XmlPullParser.START_DOCUMENT:
                        classB_Member = new ArrayList<>();
                        break;
                    case org.xmlpull.v1.XmlPullParser.START_TAG:
                        if (paser.getName().equals("classB")) {
                            isClassB = true;
                        } else if (isClassB && paser.getName().equals("classmate")) {
                            classmate = new ClassmateBeen();
                            classmate.setId(paser.getAttributeValue(0));
                            //现在位置到了<classmate>标签,跳到下一部分,也就是两个标签之间的内容
                            paser.next();
                            classmate.setClassmate(paser.getText());
                        } else {
                            break;
                        }
                        break;
                    case org.xmlpull.v1.XmlPullParser.END_TAG:
                        if (!isClassB) break;
                        if (paser.getName().equals("classB")) {
                            isClassB = false;
                        } else if (isClassB && paser.getName().equals("classmate")) {
                            classB_Member.add(classmate);
                            classmate = null;
                        }
                        break;
                }
                eventCode = paser.next();
            }
            for (ClassmateBeen c : classB_Member)
                sb.append(c.toString()+ "\r\n");
            sb.append("paser for XmlPullParser\r\n");
            return sb;
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
}


附加:XmlPullParser的中断功能,这个还没讲的,现在就基于这个demo,假如我只需要获取B班随便两个同学的信息就够了(已知B班有三个同学的信息),这是可以用上这个中断的功能,一旦获取的信息够了两个,立马中断解析,也就是中断循环。有兴趣的同学可以下载demo,然后参考下面写的模型做一下修改。把while循环内最后的一句eventCode = paser.next();改为下面的样子,中断。


中断模型:

while (eventCode != org.xmlpull.v1.XmlPullParser.END_DOCUMENT) {
               .........................省略一串代码跟上面片段一致。
      if(classB_Member!=null && classB_Member.size()==2 )
          eventCode = org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
      else   
          eventCode = paser.next();
}


demo简单的效果图:

android json原生 解析 android xml和json解析_JSON


上述代码较多,用到的片段我都放上去了,如有疑惑的话,下载demo跑一下。