Android解析XML格式数据

1.Pull解析方式

准备工作

在开始之前,需要一个服务器。如果有了,可直接跳过。在这里搭建一个本地的Apache服务器。下载地址:http://httpd.apache.org/download.cgi
进入之后按以下步骤操作:

  1. 找到Files for Micsoft Windows
    在这里插入图片描述
  2. 选择ApacheHaus
    在这里插入图片描述
    3.根据系统选择对应的版本(我选择64位的),点击图标开始下载
    在这里插入图片描述
    4.解压到自己的目录下,我的是C:\Apache\httpd-2.4.41-o111c-x86-vc15-r2\Apache24
    5.安装。找到C:\Apache\httpd-2.4.41-o111c-x86-vc15-r2\Apache24\conf文件,用记事本打开,找到:Define SRVROOT 这一项,将其右方的值改为当前你Apache安装存放的目录地址,如下所示:
    在这里插入图片描述
    继续找,找到:Listen 80
    若你的80端口被占用(可在cmd下用命令netstat -a查看),则将80端口改为别的 ,我这边改成1880。
    在这里插入图片描述
    然后用管理员身份运行cmd,进入到安装的bin目录下,输入"httpd.exe" -k install -n apache,因为我这边装过了,所以得到下面的信息

在这里插入图片描述
安装过程中有错误提示的话就去解决错误。然后在bin目录下的ApacheMonitor.exe来打开、关闭或重启服务器 。双击ApacheMonitor.exe,右下角会出现一个小图标,点击打开,然后start,启动服务器。
在这里插入图片描述
在浏览器输入http://localhost:1880/,如果出现下面界面,说明你成功了。
在这里插入图片描述
然后在C:\Apache\httpd-2.4.41-o111c-x86-vc15-r2\Apache24\htdocs的文件目录下新建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>

在manifest文件中加入权限

  <uses-permission android:name="android.permission.INTERNET" />

在gradle文件中导入OkHttp的包

 implementation 'com.squareup.okhttp3:okhttp:4.0.1'
代码

布局文件就一个按钮

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
        <Button
            android:id="@+id/btn_send_request"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="发送请求"
            />
</LinearLayout>

MainAcitivity

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "xml_json";
    Button btn_send_request;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_send_request=findViewById(R.id.btn_send_request);
        btn_send_request.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                sendRequest();
            }
        });
    }

    private void sendRequest() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                OkHttpClient client=new OkHttpClient();
                Request request=new Request.Builder()
                        .url("http://10.0.2.2:1880/get_data.xml")
                        .build();
                client.newCall(request).enqueue(new Callback() {
                    @Override
                    public void onFailure(@NotNull Call call, @NotNull IOException e) {
                    }

                    @Override
                    public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                        String respData=response.body().string();
                        parseXMLWithPull(respData);
                    }
                });

            }
        }).start();
    }
    
    private void parseXMLWithPull(String respData) {
        try {
            XmlPullParserFactory factory=XmlPullParserFactory.newInstance();
            XmlPullParser xmlPullParser=factory.newPullParser();
            //将服务器返回的数据设置进去
            xmlPullParser.setInput(new StringReader(respData));
            //得到当前的解析事件
            int evenType=xmlPullParser.getEventType();
            String id="";
            String name="";
            String version="";
            // 在while循环中不断的进行解析
            // 如果当前事件不等于xmlPullParser.END_DOCUMENT,说明解析工作还没完成,调用next()方法后可以获取下一个解析事件
            while (evenType!=xmlPullParser.END_DOCUMENT){
                //得到当前节点的名字
                String nodeName=xmlPullParser.getName();
                switch (evenType){
                    //开始解析节点
                    case  XmlPullParser.START_TAG:
                        if ("id".equals(nodeName)){
                            // 调用nextText()方法获取节点内具体的内容
                            id=xmlPullParser.nextText();
                        }else if ("name".equals(nodeName)) {
                            name = xmlPullParser.nextText();
                        } else if ("version".equals(nodeName)) {
                            version = xmlPullParser.nextText();
                        }
                        break;
                        //节点解析完成
                    case XmlPullParser.END_TAG:
                        if ("app".equals(nodeName)) {
                            Log.d(TAG, "id="+id);
                            Log.d(TAG, "name="+name);
                            Log.d(TAG, "version="+version);
                        }
                        break;
                }
                //获取下一个解析事件
                evenType=xmlPullParser.next();
            }
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

parseXMLWithPull() 方法中代码:这里首先要获取到一个XmlPullParserFactory的实例,并借助这个实例得到XmlPullParser 对象,然后调用XmlPullParser的setInput()方法将服务器返回的XML数据设置进去就可以开始解析了。解析过程:通过getEventType()可以得到当前的解析事件,然后在一个while循环中不断地进行解析,如果当前的解析事件不等于XmlPullParser.END_ DOCUMENT,说明解析工作还没完成,调用next()方法后可以获取下一个解析事件。
解析结果
在这里插入图片描述

打印不出来可能的原因
  • 用真机想要获取到localhost:1880上的数据时,不能使用10.0.2.2:1880,这是模拟器使用的,而要使用电脑的ip地址。
  • android P全面禁用了非https的连接,需要在application中加入android:networkSecurityConfig="@xml/network_security_config",如图:
 <application
       android:networkSecurityConfig="@xml/network_security_config"
       .............
 </application>

上面的@xml/network_security_config这个文件是要自己创建的,在右击你的res->new->Directory,新建一个叫xml的目录,在目录下新建一个network_security_config文件,输入以下内容:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

  • 其他错误

2.SAX解析方式

  • Pull解析方式虽然非常好用,但它并不是我们唯一的选择。SAX解析也是一种特别常用的XML解析方式,虽然它的用法比Pull解析要复杂一些,但在语义方面会更加清楚。
  • 新建一个类继承自DefaultHandler,并重写父类的5个方法,startDocument()方 法会在开始XML解析的时候调用,startElement()方法会在开始解析某个节点的时候调用, characters()方法会在获取节点中内容的时候调用,endElement()方 法会在完成解析某个节点的时候调用,endDocument()方法会在完成整个XML解析的时候调用。
  • 其中,startElement()、 characters ()和endElement()这3个方法是有参数的,从XML中解析出的数据就会以参数的形式传人到这些方法中。需要注意的是,在获取节点中的内容时,characters()方法可能会被调用多次,一些换行符也被当作内容解析出来,我们需要针对这种情况在代码中做好控制。
  • 在原有代码基础上进行修改。新建一个ContentHandler
public class ContentHandler extends DefaultHandler {
    String nodeName;
    StringBuilder id;
    StringBuilder name;
    StringBuilder version;
    @Override
    public void startDocument() throws SAXException {//开始XML解析的时候调用
        id=new StringBuilder();
        name=new StringBuilder();
        version=new StringBuilder();
    }

    @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 {//获取节点内容时调用
        //根据当前节点名判断将内容添加到哪一个StringBuilder中
        if ("id".equals(nodeName)){
            // 调用nextText()方法获取节点内具体的内容
            id.append(ch,start,length);
        }else if ("name".equals(nodeName)) {
            name.append(ch,start,length);
        } else if ("version".equals(nodeName)) {
            version.append(ch,start,length);
        }
    }
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {//完成节点解析是调用
        if ("app".equals(localName)){
            Log.d("SAX", "id="+id.toString().trim());
            Log.d("SAX", "name="+name.toString().trim());
            Log.d("SAX", "version="+version.toString().trim());
            //将StringBuilder清空
            id.setLength(0);
            name.setLength(0);
            version.setLength(0);
        }
    }

    @Override
    public void endDocument() throws SAXException {//完成整个XML解析时调用
        super.endDocument();
    }
}

更改MainActivity代码

public class MainActivity extends AppCompatActivity {
    Button btn_send_request;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_send_request=findViewById(R.id.btn_send_request);
        btn_send_request.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                sendRequest();
            }
        });
    }

    private void sendRequest() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                OkHttpClient client=new OkHttpClient();
                Request request=new Request.Builder()
                        .url("http://10.0.2.2:1880/get_data.xml")
                        .build();
                client.newCall(request).enqueue(new Callback() {
                    @Override
                    public void onFailure(@NotNull Call call, @NotNull IOException e) {
                    }

                    @Override
                    public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                        String respData=response.body().string();
                        parseXMLWithSAX(respData);
                    }
                });

            }
        }).start();
    }

    private void parseXMLWithSAX(String respData) {
        try {
            SAXParserFactory factory=SAXParserFactory.newInstance();
            XMLReader xmlReader=factory.newSAXParser().getXMLReader();
            ContentHandler handler=new ContentHandler();
            //将ContentHandler的实例设置到XMLReader中
            xmlReader.setContentHandler(handler);
            //开始执行解析
            xmlReader.parse(new InputSource(new StringReader(respData)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

打印结果
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值