Android开发学习之路--网络编程之xml、json
一般网络数据通过http来get,post,那么其中的数据不可能杂乱无章,比如我要post一段数据,肯定是要有一定的格式,协议的。常用的就是xml和json了。在此先要搭建个简单的服务器吧,首先呢下载xampp,然后安装之类的就不再多讲了,参考http://cnbin.github.io/blog/2015/06/05/mac-an-zhuang-he-shi-yong-xampp/。安装好后,启动xampp,之后在浏览器输入localhost或者127.0.0.1就可以看到如下所示了:
这个就表示服务器已经运行了,具体的代码都是放在这个/Applications/XAMPP/htdocs目录下的。然后待会儿编写个xml文件也放在这里。 编写个简单的xml文件吧。
<Person>
2 <Teacher>
3 <name>xiao hong</name>
4 <age>25</age>
5 <sex>woman</sex>
6 <class>english</class>
7 </Teacher>
8 <Student>
9 <name>xiao ming</name>
10 <age>15</age>
11 <sex>man</sex>
12 </Student>
13 </Person>
14
显示的内容就是我们文件的内容,接下去通过app去获取这个信息。
xml解析主要有三种方式,SAX,Pull,Dom。下面就用这几种方法来实现下。
首先是SAX方式,SAX方式主要是两部分组成,一部分是解析器,也就是XMLReader接口,负责读取XML文档,另一部分是事件处理器ContentHandler,负责对发送事件响应和进行XML文档处理。
继承DefaultHandler,并重写5个父类的方法。
1、startDocument方法:开始XML解析的时候调用。
2、startElement方法:开始解析某个节点的时候调用。
3、characters方法:获取节点内容的时候调用。
4、endElement方法:解析完某个节点的时候调用。
5、endDocument方法:完成XML解析时候调用。
先新建类SAXContentHandler类,继承DefaultHandler类,编写代码如下:
package com.jared.emxmlstudy; import android.util.Log; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * Created by jared on 16/2/19. */ public class SAXContentHandler extends DefaultHandler { private static final String TAB = "SAXContentHandler"; private String nodeName; private StringBuffer mName; private StringBuffer mAge; private StringBuffer mSex; private StringBuffer mClass; @Override public void startDocument() throws SAXException { mName = new StringBuffer(); mAge = new StringBuffer(); mSex = new StringBuffer(); mClass = new StringBuffer(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { nodeName = localName; } @Override public void characters(char[] ch, int start, int length) throws SAXException { if("name".equals(nodeName)) { mName.append(ch, start, length); }else if("age".equals(nodeName)) { mAge.append(ch, start, length); } else if("sex".equals(nodeName)) { mSex.append(ch, start, length); } else if("class".equals(nodeName)) { mClass.append(ch, start, length); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if("Teacher".equals(localName)) { Log.d(TAB, "This is Teacher"); Log.d(TAB, "name is:" + mName.toString().trim()); Log.d(TAB, "age is:" + mAge.toString().trim()); Log.d(TAB, "sex is:" + mSex.toString().trim()); Log.d(TAB, "class is:" + mClass.toString().trim()); mName.setLength(0); mAge.setLength(0); mSex.setLength(0); mClass.setLength(0); } else if("Student".equals(localName)) { Log.d(TAB, "This is Student"); Log.d(TAB, "name is:" + mName.toString().trim()); Log.d(TAB, "age is:" + mAge.toString().trim()); Log.d(TAB, "sex is:" + mSex.toString().trim()); mName.setLength(0); mAge.setLength(0); mSex.setLength(0); } } @Override public void endDocument() throws SAXException { } }
这里要使用三种方法,所以修改布局如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_margin="10dp" tools:context="com.jared.emxmlstudy.MainActivity"> <Button android:id="@+id/getXmlsax" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="SAX方式获取"/> <Button android:id="@+id/getXmlpull" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Pull方式获取"/> <Button android:id="@+id/getXmldom" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Dom方式获取"/> </LinearLayout>
然后MainActivity中添加代码如下;
package com.jared.emxmlstudy; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import com.loopj.android.http.AsyncHttpClient; import com.loopj.android.http.AsyncHttpResponseHandler; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import java.io.StringReader; import javax.xml.parsers.SAXParserFactory; import cz.msebera.android.httpclient.Header; public class MainActivity extends AppCompatActivity { private static final String xmlUrl = "http://192.168.1.102/test/person.xml"; private Button mGetXmlSax; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mGetXmlSax = (Button)findViewById(R.id.getXmlsax); mGetXmlSax.setOnClickListener(new myOnClickListener()); } private class myOnClickListener implements View.OnClickListener { @Override public void onClick(View view) { switch (view.getId()) { case R.id.getXmlsax: sendRequestWithAsyncHttpClient(xmlUrl); break; default: break; } } } private void sendRequestWithAsyncHttpClient(String url) { AsyncHttpClient client = new AsyncHttpClient(); client.get(url, new AsyncHttpResponseHandler() { @Override public void onSuccess(int i, Header[] headers, byte[] bytes) { try { String response = new String(bytes, 0, bytes.length, "utf-8"); parseXMLWithSax(response); } catch (Exception e) { e.printStackTrace(); } } @Override public void onFailure(int i, Header[] headers, byte[] bytes, Throwable throwable) { } }); } private void parseXMLWithSax(String xmlData) { try { SAXParserFactory factory = SAXParserFactory.newInstance(); XMLReader xmlReader = factory.newSAXParser().getXMLReader(); SAXContentHandler mHandler = new SAXContentHandler(); xmlReader.setContentHandler(mHandler); xmlReader.parse(new InputSource(new StringReader(xmlData))); } catch (Exception e) { e.printStackTrace(); } } }
这里用到了AsyncHttpClient,具体库的添加参考上一篇文章Android开发学习之路--网络编程之初体验。好了,这里在发送请求的时候,成功再调用parseXMLWithSax进行解析。这里的地址是192.168.1.102,因为是真机调试,连接到了同一个网段,然后手机就可以访问我们的服务器了。
实例化一个factory,通过XMLReader来读取解析。运行点击按钮如下显示:
02-19 21:01:58.661 17390-17390/? D/SAXContentHandler: This is Teacher 02-19 21:01:58.661 17390-17390/? D/SAXContentHandler: name is:xiao hong 02-19 21:01:58.661 17390-17390/? D/SAXContentHandler: age is:25 02-19 21:01:58.661 17390-17390/? D/SAXContentHandler: sex is:woman 02-19 21:01:58.661 17390-17390/? D/SAXContentHandler: class is:english 02-19 21:01:58.661 17390-17390/? D/SAXContentHandler: This is Student 02-19 21:01:58.661 17390-17390/? D/SAXContentHandler: name is:xiao ming 02-19 21:01:58.661 17390-17390/? D/SAXContentHandler: age is:15 02-19 21:01:58.661 17390-17390/? D/SAXContentHandler: sex is:man
可以发现已经得到我们想要的信息了。
接着使用Pull方式,开始解析可以通过调用它的next方法,获取下一个事件,可以通过getAttribute方法获取属性,通过nextText方法来获取节点的值。编写代码如下:
package com.jared.emxmlstudy; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import com.loopj.android.http.AsyncHttpClient; import com.loopj.android.http.AsyncHttpResponseHandler; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserFactory; import java.io.StringReader; import javax.xml.parsers.SAXParserFactory; import cz.msebera.android.httpclient.Header; public class MainActivity extends AppCompatActivity { private static final String TAB = "XMLParse"; private static final String xmlUrl = "http://192.168.1.102/test/person.xml"; private Button mGetXmlSax; private Button mGetXmlPull; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mGetXmlSax = (Button)findViewById(R.id.getXmlsax); mGetXmlPull = (Button)findViewById(R.id.getXmlpull); mGetXmlSax.setOnClickListener(new myOnClickListener()); mGetXmlPull.setOnClickListener(new myOnClickListener()); } private class myOnClickListener implements View.OnClickListener { @Override public void onClick(View view) { switch (view.getId()) { case R.id.getXmlsax: sendRequestWithSax(xmlUrl); break; case R.id.getXmlpull: sendRequestWithPull(xmlUrl); break; default: break; } } } private void sendRequestWithPull(String url) { AsyncHttpClient client = new AsyncHttpClient(); client.get(url, new AsyncHttpResponseHandler() { @Override public void onSuccess(int i, Header[] headers, byte[] bytes) { try { String response = new String(bytes, 0, bytes.length, "utf-8"); parseXMLWithPull(response); } catch (Exception e) { e.printStackTrace(); } } @Override public void onFailure(int i, Header[] headers, byte[] bytes, Throwable throwable) { } }); } private void parseXMLWithPull(String xmlData) { try { XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); XmlPullParser xmlPullParser = factory.newPullParser(); xmlPullParser.setInput(new StringReader(xmlData)); int eventType = xmlPullParser.getEventType(); String mName = ""; String mAge = ""; String mSex = ""; String mClass = ""; while (eventType != XmlPullParser.END_DOCUMENT) { String nodeName = xmlPullParser.getName(); switch (eventType) { case XmlPullParser.START_TAG: { if("name".equals(nodeName)) { mName = xmlPullParser.nextText(); } else if("age".equals(nodeName)) { mAge = xmlPullParser.nextText(); } else if("sex".equals(nodeName)) { mSex = xmlPullParser.nextText(); } else if("class".equals(nodeName)) { mClass = xmlPullParser.nextText(); } break; } case XmlPullParser.END_TAG: { if("Teacher".equals(nodeName)) { Log.d(TAB, "This is Teacher"); Log.d(TAB, "name is:" + mName.trim()); Log.d(TAB, "age is:" + mAge.trim()); Log.d(TAB, "sex is:" + mSex.trim()); Log.d(TAB, "class is:" + mClass.trim()); } else if("Student".equals(nodeName)) { Log.d(TAB, "This is Student"); Log.d(TAB, "name is:" + mName.trim()); Log.d(TAB, "age is:" + mAge.trim()); Log.d(TAB, "sex is:" + mSex.trim()); } } default: break; } eventType = xmlPullParser.next(); } } catch (Exception e) { e.printStackTrace(); } } private void sendRequestWithSax(String url) { AsyncHttpClient client = new AsyncHttpClient(); client.get(url, new AsyncHttpResponseHandler() { @Override public void onSuccess(int i, Header[] headers, byte[] bytes) { try { String response = new String(bytes, 0, bytes.length, "utf-8"); parseXMLWithSax(response); } catch (Exception e) { e.printStackTrace(); } } @Override public void onFailure(int i, Header[] headers, byte[] bytes, Throwable throwable) { } }); } private void parseXMLWithSax(String xmlData) { try { SAXParserFactory factory = SAXParserFactory.newInstance(); XMLReader xmlReader = factory.newSAXParser().getXMLReader(); SAXContentHandler mHandler = new SAXContentHandler(); xmlReader.setContentHandler(mHandler); xmlReader.parse(new InputSource(new StringReader(xmlData))); } catch (Exception e) { e.printStackTrace(); } } }
运行结果如下:
02-19 21:52:01.391 19388-19388/? D/XMLParse: This is Teacher 02-19 21:52:01.391 19388-19388/? D/XMLParse: name is:xiao hong 02-19 21:52:01.391 19388-19388/? D/XMLParse: age is:25 02-19 21:52:01.391 19388-19388/? D/XMLParse: sex is:woman 02-19 21:52:01.391 19388-19388/? D/XMLParse: class is:english 02-19 21:52:01.391 19388-19388/? D/XMLParse: This is Student 02-19 21:52:01.391 19388-19388/? D/XMLParse: name is:xiao ming 02-19 21:52:01.391 19388-19388/? D/XMLParse: age is:15 02-19 21:52:01.391 19388-19388/? D/XMLParse: sex is:man
最后一种是Dom方式,Dom方式主要比较耗费内存,需要遍历所有,一般手机上的app开发不太适用。那就简单实现下吧,还是利用Async-HttpClient,接着编写代码如下:
private void parseXMLWithDom(String xmlData) { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); String mName = ""; String mAge = ""; String mSex = ""; String mClass = ""; try { DocumentBuilder builder = factory.newDocumentBuilder(); //Document document = builder.parse(xmlData); Document document = builder.parse(new InputSource(new StringReader(xmlData))); Element root = document.getDocumentElement(); Log.d(TAB, "根节点名称:" + root.getTagName()); NodeList items = root.getElementsByTagName("Teacher"); Element personElement = (Element)items.item(0); Log.d(TAB, "根节点名称:" + personElement.getTagName()); NodeList childNodes = personElement.getChildNodes(); Log.d(TAB, "This is Teacher"); for(int i = 0; i < childNodes.getLength(); i++) { Node grandElement = childNodes.item(i); if(grandElement.getNodeType() == Node.ELEMENT_NODE) { if("name".equals(grandElement.getNodeName())) { mName = grandElement.getFirstChild().getNodeValue(); Log.d(TAB, "name is:" + mName.trim()); } else if("age".equals(grandElement.getNodeName())) { mAge = grandElement.getFirstChild().getNodeValue(); Log.d(TAB, "age is:" + mAge.trim()); } else if("sex".equals(grandElement.getNodeName())) { mSex = grandElement.getFirstChild().getNodeValue(); Log.d(TAB, "sex is:" + mSex.trim()); } else if("class".equals(grandElement.getNodeName())) { mClass = grandElement.getFirstChild().getNodeValue(); Log.d(TAB, "class is:" + mClass.trim()); } } } } catch (Exception e){ e.printStackTrace(); } }
这里简单的就实现了Teacher的,student的就没有添加,相信也是很容易的了,其余的代码和上述类似,运行效果如下:
02-20 09:16:49.601 1353-1353/? D/XMLParse: 根节点名称:Person 02-20 09:16:49.601 1353-1353/? D/XMLParse: 根节点名称:Teacher 02-20 09:16:49.601 1353-1353/? D/XMLParse: This is Teacher:9 02-20 09:16:49.601 1353-1353/? D/XMLParse: name is:xiao hong 02-20 09:16:49.601 1353-1353/? D/XMLParse: age is:25 02-20 09:16:49.601 1353-1353/? D/XMLParse: sex is:woman 02-20 09:16:49.601 1353-1353/? D/XMLParse: class is:english
关于xml基本上先学习这些知识了。接着学习json的知识。
首先和xml一样,新建一个person.json文件,如下:
1 [{"name":"xiao hong", "age":"25", "sex":"wonan"},
2 {"name":"xiao ming", "age":"15", "sex":"man"},
3 {"name":"xiao qiang", "age": 30, "sex":"man"}]
保存到和xml同一级目录下,运行浏览器如下图所示:
如图可知配置已经ok了,那么接下来就开始完成代码了,这里要使用JSONObject和GSON来实现,布局如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_margin="10dp" tools:context="com.jared.emjsonstudy.MainActivity"> <Button android:id="@+id/getJSONObject" android:text="Get Json With JSONObject" android:layout_width="match_parent" android:layout_height="wrap_content" android:textAllCaps="false"/> <Button android:id="@+id/getGSON" android:text="Get Json With GSON" android:layout_width="match_parent" android:layout_height="wrap_content" android:textAllCaps="false"/> </LinearLayout>
接着实现代码,还是用了Async-HttpClient来实现,代码如下:
package com.jared.emjsonstudy; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import com.loopj.android.http.AsyncHttpClient; import com.loopj.android.http.AsyncHttpResponseHandler; import org.json.JSONArray; import org.json.JSONObject; import cz.msebera.android.httpclient.Header; public class MainActivity extends AppCompatActivity { private static final String TAB = "JSONStudy"; private static final String JSON_URL = "http://192.168.1.102/test/person.json"; private Button mGetJSONObjectBtn; private Button mGetGSONBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mGetJSONObjectBtn = (Button)findViewById(R.id.getJSONObject); mGetGSONBtn = (Button)findViewById(R.id.getGSON); mGetJSONObjectBtn.setOnClickListener(new myOnClickListener()); mGetGSONBtn.setOnClickListener(new myOnClickListener()); } private class myOnClickListener implements View.OnClickListener { @Override public void onClick(View view) { switch (view.getId()) { case R.id.getJSONObject: getJSONWithJSONObject(JSON_URL); break; case R.id.getGSON: break; default: break; } } } private void parseJSONWithJSONObject(String jsonData) { try { JSONArray jsonArray = new JSONArray(jsonData); for (int i = 0; i < jsonArray.length(); i++) { JSONObject jsonObject = jsonArray.getJSONObject(i); String mName = jsonObject.getString("name"); String mAge = jsonObject.getString("age"); String mSex = jsonObject.getString("sex"); Log.d(TAB, "name is: " + mName); Log.d(TAB, "age is: " + mAge); Log.d(TAB, "sex is:" + mSex); } } catch (Exception e) { e.printStackTrace(); } } private void getJSONWithJSONObject(String url) { AsyncHttpClient client = new AsyncHttpClient(); client.get(url, new AsyncHttpResponseHandler() { @Override public void onSuccess(int i, Header[] headers, byte[] bytes) { try { String response = new String(bytes, 0, bytes.length, "utf-8"); parseJSONWithJSONObject(response); } catch (Exception e) { e.printStackTrace(); } } @Override public void onFailure(int i, Header[] headers, byte[] bytes, Throwable throwable) { } }); } }
如上代码明显比xml的简单多了,JSONArray获取到Json数据,然后通过JSONObject来获取对应键值的内容。因为我的电脑ip地址是192.168.1.102,手机和电脑在同一个网段,所以直接利用真机来测试,效果如下:
02-20 10:13:20.521 4947-4947/? D/JSONStudy: name is: xiao hong 02-20 10:13:20.521 4947-4947/? D/JSONStudy: age is: 25 02-20 10:13:20.521 4947-4947/? D/JSONStudy: sex is:wonan 02-20 10:13:20.521 4947-4947/? D/JSONStudy: name is: xiao ming 02-20 10:13:20.521 4947-4947/? D/JSONStudy: age is: 15 02-20 10:13:20.521 4947-4947/? D/JSONStudy: sex is:man 02-20 10:13:20.521 4947-4947/? D/JSONStudy: name is: xiao qiang 02-20 10:13:20.521 4947-4947/? D/JSONStudy: age is: 30 02-20 10:13:20.521 4947-4947/? D/JSONStudy: sex is:man
接着使用google的开源库GSON来实现,用到开源库,那就先下载了。gson下载地址:GSON下载地址点击这里。gson gitbub地址:https://github.com/google/gson。
GSON库主要是将一段JSON格式的字符串自动映射成一个对象,从而不需要编写代码去解析。这里新建一个Person类来获取数据,代码如下:
package com.jared.emjsonstudy; /** * Created by jared on 16/2/20. */ public class Person { private String name; private String age; private String sex; public String getName() { return name; } public String getAge() { return age; } public String getSex() { return sex; } public void setName(String name) { this.name = name; } public void setAge(String age) { this.age = age; } public void setSex(String sex) { this.sex = sex; } }
MainActivity中添加代码:
void parseJSONWithGSON(String jsonData) { Gson gson = new Gson(); List<Person> personList = gson.fromJson(jsonData, new TypeToken<List<Person>>(){}.getType()); for (Person person :personList) { Log.d(TAB, "Gson: name is: " + person.getName()); Log.d(TAB, "Gson: age is: " + person.getAge()); Log.d(TAB, "Gson: sex is:" + person.getSex()); } } void getJSONWithGSON(String url) { AsyncHttpClient client = new AsyncHttpClient(); client.get(url, new AsyncHttpResponseHandler() { @Override public void onSuccess(int i, Header[] headers, byte[] bytes) { try { String response = new String(bytes, 0, bytes.length, "utf-8"); parseJSONWithGSON(response); } catch (Exception e) { e.printStackTrace(); } } @Override public void onFailure(int i, Header[] headers, byte[] bytes, Throwable throwable) { } }); }
这里new了一个Gson,然后通过fromJson的方法,通过TypeToken获取数据并保存到Person列表中。运行看下效果:
02-20 10:42:56.381 6434-6434/? D/JSONStudy: Gson: name is: xiao hong 02-20 10:42:56.381 6434-6434/? D/JSONStudy: Gson: age is: 25 02-20 10:42:56.381 6434-6434/? D/JSONStudy: Gson: sex is:wonan 02-20 10:42:56.381 6434-6434/? D/JSONStudy: Gson: name is: xiao ming 02-20 10:42:56.381 6434-6434/? D/JSONStudy: Gson: age is: 15 02-20 10:42:56.381 6434-6434/? D/JSONStudy: Gson: sex is:man 02-20 10:42:56.381 6434-6434/? D/JSONStudy: Gson: name is: xiao qiang 02-20 10:42:56.381 6434-6434/? D/JSONStudy: Gson: age is: 30 02-20 10:42:56.381 6434-6434/? D/JSONStudy: Gson: sex is:man当然如果想要生存json数据,也是可以用gson的tojson方法的。