一般我们会在网络上传输一些格式化后的数据,这种数据会有一定的语法结构规则和语义,当另一方收到数据消息之后,就可以按照相同的结构规则进行解析,从而取出想要的那部分内容。随便传递一段文本肯定是不行的,因为另一方根本就不知道这段文本的用途是什么。
在网络上传输数据时最常用的格式有两种:XML 和 JSON。
为了方便测试,首先在一个服务器上提供一段 XML 文本,然后在程序里去访问它,再对得到的 XML 文本进行解析。我使用的是 Mac 系统,自带了 Apache 服务器,可在这里查看配置方法 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // 测试文件:get_data.xml <apps > <app > <id > 1</id > <name > Google Maps</name > <version > 1.0</version > </app > <app > <id > 2</id > <name > Chrome</name > <version > 2.1</version > </app > <app > <id > 3</id > <name > Google Play</name > <version > 2.3</version > </app > </apps >
解析 XML 格式数据
常用的解析 XML 格式数据的方式有两种:Pull 解析和 SAX 解析。
Pull 解析方式 示例(Kotlin):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 class NetworkActivity : BaseActivity () { private val tag = "TAG_NetworkActivity" override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_network) initView() } private fun initView () { btnPull.setOnClickListener{ pullXMLWithOkHttp() } } private fun pullXMLWithOkHttp () { thread { try { val client = OkHttpClient() val request = Request.Builder() .url("http://10.0.2.2/get_data.xml" ) .build() val response = client.newCall(request).execute() val responseData = response.body?.string() if (responseData != null ){ showResponse(responseData) parseXMLWithPull(responseData) } }catch (e:Exception){ e.printStackTrace() } } } private fun parseXMLWithPull (xmlData: String ) { try { val factory = XmlPullParserFactory.newInstance() val xmlPullParser = factory.newPullParser() xmlPullParser.setInput(StringReader(xmlData)) var eventType = xmlPullParser.eventType var id = "" var name = "" var version = "" while (eventType != XmlPullParser.END_DOCUMENT){ val nodeName = xmlPullParser.name when (eventType){ XmlPullParser.START_TAG -> { when (nodeName){ "id" -> id = xmlPullParser.nextText() "name" -> name = xmlPullParser.nextText() "version" -> version = xmlPullParser.nextText() } } XmlPullParser.END_TAG -> { if ("app" == nodeName){ Log.e(tag,"id is $id " ) Log.e(tag,"name is $name " ) Log.e(tag,"version is $version " ) } } } eventType = xmlPullParser.next() } }catch (e:Exception){ e.printStackTrace() } } private fun showResponse (response: String ) { runOnUiThread{ tvRequest.text = response } } }
注:从 Android 9.0 系统开始,HTTP 类型的网络请求需要做些处理。
SAX 解析方式 SAX 解析的用法比 Pull 解析要复杂一些,但在语义方面会更加清楚。使用 SAX 解析,通常情况下会新建一个类继承自 DefaultHandler,并重写父类的 5 个方法。
示例(Kotlin):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 class MyHandler : DefaultHandler (){ private var nodeName = "" private lateinit var id : StringBuilder private lateinit var name : StringBuilder private lateinit var version : StringBuilder override fun startDocument () { id = StringBuilder() name = StringBuilder() version = StringBuilder() } override fun startElement (uri: String , localName: String , qName: String , attributes: Attributes ) { nodeName = localName Log.d("TAG" ,"uri is $uri " ) Log.d("TAG" ,"localName is $localName " ) Log.d("TAG" ,"qName is $qName " ) Log.d("TAG" ,"attributes is $attributes " ) } override fun characters (ch: CharArray ?, start: Int , length: Int ) { when (nodeName){ "id" -> id.append(ch,start,length) "name" -> name.append(ch,start,length) "version" -> version.append(ch,start,length) } } override fun endElement (uri: String ?, localName: String ?, qName: String ?) { if ("app" == localName){ Log.e("TAG" ,"id is ${id.toString().trim()} " ) Log.e("TAG" ,"name is ${name.toString().trim()} " ) Log.e("TAG" ,"version is ${version.toString().trim()} " ) id.setLength(0 ) name.setLength(0 ) version.setLength(0 ) } } override fun endDocument () { } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 class NetworkActivity : BaseActivity () { private val tag = "TAG_NetworkActivity" override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_network) initView() } private fun initView () { btnSAX.setOnClickListener{ saxXMLWithOkHttp() } } private fun saxXMLWithOkHttp () { thread { try { val client = OkHttpClient() val request = Request.Builder() .url("http://10.0.2.2/get_data.xml" ) .build() val response = client.newCall(request).execute() val responseData = response.body?.string() if (responseData != null ){ parseXMLWithSAX(responseData) } }catch (e:Exception){ e.printStackTrace() } } } private fun parseXMLWithSAX (xmlData: String ) { try { val factory = SAXParserFactory.newInstance() val xmlReader = factory.newSAXParser().xmlReader val handler = MyHandler() xmlReader.contentHandler = handler xmlReader.parse(InputSource(StringReader(xmlData))) }catch (e:Exception){ e.printStackTrace() } } }
DOM 解析
解析 JSON 格式数据
比起 XML,JSON 的主要优势在于它的体积更小,在网络上传输的时候更省流量。但缺点在于,它的语义性较差,看起来不如 XML 直观。
1 2 3 4 [ { "id" : "5" , "version" : "5.5" , "name" : "Clash of Clans" } , { "id" : "6" , "version" : "7.0" , "name" : "Boom Beach" } , { "id" : "7" , "version" : "3.5" , "name" : "Clash Royale" } ]
使用 JSONObject JSONObject 是谷歌官方提供的。
示例(Kotlin):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class NetworkActivity : BaseActivity () { private val tag = "TAG_NetworkActivity" override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_network) initView() } private fun initView () { btnJSONObject.setOnClickListener{ jsonObjectWithOkHttp() } } private fun jsonObjectWithOkHttp () { thread { try { val client = OkHttpClient() val request = Request.Builder() .url("http://10.0.2.2/get_data.json" ) .build() val response = client.newCall(request).execute() val responseData = response.body?.string() if (responseData != null ){ parseJSONWithJSONObject(responseData) } }catch (e:Exception){ e.printStackTrace() } } } private fun parseJSONWithJSONObject (jsonData: String ) { try { val jsonArray = JSONArray(jsonData) for (i in 0 until jsonArray.length()){ val jsonObject = jsonArray.getJSONObject(i) val id = jsonObject.getString("id" ) val name = jsonObject.getString("name" ) val version = jsonObject.getString("version" ) Log.d(tag,"id is $id " ) Log.d(tag,"name is $name " ) Log.d(tag,"version is $version " ) } }catch (e:Exception){ e.printStackTrace() } } }
使用 GSON GSON 是 Google 提供的开源库,可以让解析 JSON 数据的工作变得更简单。
1 2 implementation "com.google.code.gson:gson:2.8.5"
GSON 的强大之处在于可以将一段 JSON 格式的字符串自动映射成一个对象,从而不需要我们再手动编写代码进行解析了。
比如一段 JSON 格式数据:{“name”:”Tom”, “age”:20}。便可以定义一个 Person 类,并加入 name 和 age 字段,然后调用如下代码将 JSON 数据自动解析成一个 Person 对象。
1 2 val gson = Gson()val person = gson.fromJson(jsonData, Person::class .java)
如果需要解析一段 JSON 数组格式数据:这时需要借助 TypeToken 将期望解析成的数据类型传入 fromJson()。
[{“name”:”Tom”, “age”:20}, {“name”:”Tom”, “age”:20}, {“name”:”Tom”, “age”:20}]
1 2 val typeOf = object :TypeToken<List<Person>>(){}.typeval person = gson.fromJson<List<Person>>(jsonData, typeOf)
示例(Kotlin):
1 2 3 4 class AppBean (val id: String, val name: String, val version: String)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class NetworkActivity : BaseActivity () { private val tag = "TAG_NetworkActivity" override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_network) initView() } private fun initView () { btnGSON.setOnClickListener{ gsonWithOkHttp() } } private fun gsonWithOkHttp () { thread { try { val client = OkHttpClient() val request = Request.Builder() .url("http://10.0.2.2/get_data.json" ) .build() val response = client.newCall(request).execute() val responseData = response.body?.string() if (responseData != null ){ parseJSONWithGSON(responseData) } }catch (e:Exception){ e.printStackTrace() } } } private fun parseJSONWithGSON (jsonData: String ) { val gson = Gson() val typeOf = object : TypeToken<List<AppBean>>(){}.type val appList = gson.fromJson<List<AppBean>>(jsonData, typeOf) for (app in appList){ Log.d(tag,"id is ${app.id} " ) Log.d(tag,"name is ${app.name} " ) Log.d(tag,"version is ${app.version} " ) } } }
备注
参考资料 :
第一行代码(第3版)
欢迎关注微信公众号:非也缘也