Android解析服务器响应数据

Posted z啵唧啵唧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android解析服务器响应数据相关的知识,希望对你有一定的参考价值。

文章目录

android解析服务器响应数据

解析XML格式数据

Pull解析方式

  • 解析XML格式的数据其实也有挺多种,比较常用的两种是Pull解析和SAX解析
  • 修改MainActivity中的代码

    /**
     * 使用Pull的方式解析XML数据
     *
     * @param xmlData String
     */
    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.d("MainActivity", "id is $id")
                            Log.d("MainActivity", "name is $name")
                            Log.d("MainActivity", "version is $version")
                        
                    
                
                eventType = xmlPullParser.next()
            
         catch (e: Exception) 
            e.printStackTrace()
        
    
  • 在这里首先将HTTP请求的地址改成了http://10.0.2.2/get_data.xml,10.0.2.2 因为10.0.2.2对于模拟器来说就是本计算机的IP地址,在得到服务器返回的数据之后,我们不再直接将其进行展示,而是调用了parseXMLWithPull()方法解析服务器返回的数据.
  • 因为从Android9.0系统开始,应用程序默认只允许使用HTTPS类型的网络请求,HTTP类型的网络请求因为有安全隐患默认不再被支持,而搭建的Apache服务器使用的就是HTTP,所在还需要再进行一项额外的配置.
  • 为了能够让程序使用HTTP,还需要在res目录下New->Directory,创建一个xml目录,接着右击xml目录->New->File,创建一个network_config.xml文件,然后修改network_config.xml文件中的内容,如下所示
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system"/>
        </trust-anchors>
    </base-config>
</network-security-config>
  • 这段配置文件的意思是允许我们以明文的方式在网络中传输数据,因为HTTP就是以明文的方式传输数据的
  • 接下来修改AndroidManifest.xml中的代码来启用刚创建的配置文件

  • 这样就可以在程序中使用HTTP协议了,现在运行项目,可以看到,已经将XML数据中的指定内容成功解析出来了

SAX解析方式

  • Pull解析方式虽然好用,但是并不是唯一的选择,还有一种SAX解析方式也是最常用的一种解析方式
  • 要使用SAX解析方式,通常情况下需要新建一个类,继承自DefaultHandler,并重写父类的5个方法
class MyHandler : DefaultHandler() 
    override fun startDocument()    
    
    
    override fun startElement(uri: String, localName: String, qName: String, attributes: Attributes)    
    
    
    override fun characters(ch: CharArray, start: Int, length: Int)    
    
    
    override fun endElement(uri: String, localName: String, qName: String)    
    
    
    override fun endDocument()    
    

  • 新建一个ContentHandler类继承自DefaultHandler,并重写5个方法
package com.zb.networktest

import android.util.Log
import org.xml.sax.Attributes
import org.xml.sax.helpers.DefaultHandler
import kotlin.text.StringBuilder

/**
 * @Description:
 * @Author zb~
 * @Date 2022/12/26 14:15
 */
class ContentHandler : DefaultHandler() 
    private var nodeName = ""
    private lateinit var id: StringBuilder
    private lateinit var name: StringBuilder
    private lateinit var version: StringBuilder

    /**
     * 该方法在开始XML解析的时候进行调用
     */
    override fun startDocument() 
        id = StringBuilder()
        name = StringBuilder()
        version = StringBuilder()
    

    /**
     * 该方法在开始解析某一个节点的时候进行调用
     *
     * @param uri String
     * @param localName String
     * @param qName String
     * @param attributes Attributes
     */
    override fun startElement(
        uri: String?,
        localName: String,
        qName: String?,
        attributes: Attributes?
    ) 
        //记录当前节点名
        nodeName = localName
        Log.d("ContentHandler", "uri is $uri")
        Log.d("ContentHandler", "localName is $localName")
        Log.d("ContentHandler", "qName is $qName")
        Log.d("ContentHandler", "attributes is $attributes")
    

    /**
     * 该方法会在获取节点种内容的时候进行调用
     *
     * @param ch CharArray
     * @param start Int
     * @param length Int
     */
    override fun characters(ch: CharArray?, start: Int, length: Int) 
        //根据当前节点名判断将内容添加到哪一个StringBuilder当中
        when (nodeName) 
            "id" -> id.append(ch, start, length)
            "name" -> name.append(ch, start, length)
            "version" -> version.append(ch, start, length)
        
    

    /**
     * 该方法会在完成解析某一个节点的时候进行调用
     *
     * @param uri String
     * @param localName String
     * @param qName String
     */
    override fun endElement(uri: String?, localName: String?, qName: String?) 
        if ("app" == localName) 
            //trim()方法可以去掉字符串两端的多余空格
            Log.d("ContentHandler", "id is $id.toString().trim()")
            Log.d("ContentHandler", "name is $name.toString().trim()")
            Log.d("ContentHandler", "version is $version.toString().trim()")
            //最后要将所有的StringBuilder清空
            id.setLength(0)
            name.setLength(0)
            version.setLength(0)
        
    

    /**
     * 该方法会在完成整个XML解析的时候进行调用
     */
    override fun endDocument() 

    

  • 接下来修改MainActivity中的代码
    /**
     * 使用SAX的方式来解析XML数据
     *
     * @param xmlData String
     */
    private fun parseXMLWithSAX(xmlData: String) 
        try 
            val factory = SAXParserFactory.newInstance()
            val xmlReader = factory.newSAXParser().xmlReader
            val handler = ContentHandler()
            //将ContentHandler的实例设置到XMLReader中
            xmlReader.contentHandler = handler
            //开始执行解析
            xmlReader.parse(InputSource(StringReader(xmlData)))
         catch (e: Exception) 
            e.printStackTrace()
        
    
  • 在得到服务器返回的数据之后,通过上面这个parseXMLWithSAX()方法来解析XML数据
  • parseXMLWithSAX()方法中先是获取到了SAXParseFactory的对象,然后再获取XMLReader对象,接着我们将编写的ContentHandler的实例设置到XMLReader中,最后调用parse()方法进行解析
  • 最后运行程序,发现同样也是能够解析XML数据

解析JSON数据

  • 相比于XML数据,JSON数据的优势在于它的体积更小,在网络上传输的时候更省流量,但是它的缺点在于它的语义比较差,看起来不如XML直观

使用JSONObject

  • 解析JSON数据也有很多种方法,可以使用官方提供的JSONObject,也可以使用Google的开源GSON,另外还有一些第三方开源库比如:Jackson,FastJSON等也非常不错.
  • 先看一下JSONObject的使用方法
  • 修改MainActivity中的代码,编写相关解析方法
    /**
     * JSONObject的方式解析JSON数据
     *
     * @param jsonData Response
     */
    private fun parseJSONWithJSONObject(jsonData: Response) 
        try 
            //由于在服务器中定义的是一个JSON数组,所以首先将服务器返回的数据传入一个JSONArray对象中
            val jsonArray = JSONArray(jsonData)
            //然后循环遍历这个JSONArray,从中取出的每一个元素都是JSONObject对象
            for (i in 0 until jsonArray.length()) 
                //然后循环遍历这个JSONArray,从中取出的每一个元素都是JSONObject对象
                val jsonObject = jsonArray.getJSONObject(i)
                //每个JSONObject对象中又会包含id,name,version这些数据,接下来只需要调用getString()方法将这些数据进行取出才可以
                val id = jsonObject.getString("id")
                val name = jsonObject.getString("name")
                val version = jsonObject.getString("version")
                //将上述数据打印出来
                Log.d("MainActivity", "id is $id")
                Log.d("MainActivity", "name is $name")
                Log.d("MainActivity", "version is $version")
            
         catch (e: Exception) 
            e.printStackTrace()
        
    
  • 运行程序即可发现成功解析JSON数据

使用GSON的方式来解析JSON数据

  • 使用JSONObject来解析JSON数据是非常的简单,但是使用GSON来进行数据的解析,同样也是十分的简单
  • 因为GSON并没有添加到Android官方的API当中,所以想要使用这个功能,需要在项目当中添加GSON的依赖,添加方式如下
implementation("com.google.code.gson:gson:2.8.9")

  • GSON的强大之处在于,可以将一段JSON格式的字符串自动映射成为一个对象,从而不需要我们再动手编写代码进行解析了
  • 比如一段JSON格式的数据如下所示:

    "name:"Tom", "age":"20"

  • 我们就可以定义一个JSON类,并加入name和age字段,然后只需要简单的调用如下代码就可以将JSON数据自动解析成为一个Person对象了
val gson = Gson()
val person = gson.fromJson(jsonData, Person::class.java)
  • 但是如果需要解析的是一段json数组,会比较麻烦,比如如下的格式
[
    
        "name":"Tom",
        "age":"20"
    ,
    
    	"name":"Jack",
        "age":"18"
    ,
    
        "name":"Lily",
        "age":"22"
    
]
  • 这个时候,我们需要借助TypeToken将期望解析成的数据类型传入fromJson()方法中,如下所示:
val typeOf = object : TypeToken<List<Person>>() .type
val people = gson.fromJson<List<Person>>(jsonData, typeOf)
  • 综上所述就是GSON的基本用法,下面新建一个App类来解析Apache的json数据
class App(val id: String, val name: String, val version: String)
  • 然后修改MainActivity当中的代码,编写GSON方法具体的解析逻辑
    /**
     * 使用GSON来解析Json数据
     *
     * @param jsonData String
     */
    private fun parseJSONWithGSON(jsonData: String) 
        val gson = Gson()
        val typeOf = object : TypeToken<List<App>>() .type
        val appList = gson.fromJson<List<App>>(jsonData, typeOf)
        for (app in appList) 
            Log.d("MainActivity", "id id $app.id")
            Log.d("MainActivity", "name id $app.name")
            Log.d("MainActivity", "id id $app.id")
        
    
  • 所实现的效果和JSONObject是一样的.

以上是关于Android解析服务器响应数据的主要内容,如果未能解决你的问题,请参考以下文章

一次 HTTP 请求响应过程的完整解析

Webserver 到 android XML 解析

http

如何从android中的php关联数组解析JSON响应?

解析从 android onMessageReceived 上的 fcm 响应获得的 json

完整的一次 HTTP 请求响应过程