Xml 格式数据的生成和解析

本文介绍了XML的基本概念,包括XML的起源、W3C组织及其作用。重点讲解了XML与HTML、属性文件的区别,以及XML文档的组成部分,如文档声明、转义字符和CDATA区。此外,还探讨了XML在Android中的应用,通过实例展示了如何生成和解析XML文件,包括使用XmlSerializer和Pull解析器的方法,并对比了Pull解析与SAX解析的异同。
部署运行你感兴趣的模型镜像

相关阅读

  1. XML约束
  2. Xml 格式数据的生成和解析
  3. XML解析器

什么是XML

XML全称为Extensible Markup Language, 意思是可扩展的标记语言,它是 SGML(标准通用标记语言)的一个子集。

XML语法上和HTML比较相似,但HTML中的元素是固定的,而XML的标签是可以由用户自定义的。

W3C在1998年2月发布1.0版本;
W3C在2004年2月发布1.1版本,但因为1.1版本不能向下兼容1.0版本,所以1.1没有人用。同时,在2004年2月W3C又发布了1.0版本的第三版。我们要学习的还是1.0版本!!!

W3C组织

W3C是万维网联盟(World Wide Web Consortium)英文的缩写,它成立于1994年10月,以开放论坛的方式来促进开发互通技术(包括规格、指南、软件和工具),开发网络的全部潜能。万维网联盟(W3C)从1994年成立以来,已发布了90多份Web技术规范,领导着Web技术向前发展。

W3C认为自身不是官方组织,因此将它正式发布的规范称为推荐(建议)标准,意思是进一步标准化的建议,但是由于组织自身的权威性往往成为事实上的标准。

XML的作用

  • 程序的配置文件(这也是最后大家使用XML最常见的目的);
  • 数据交换:不同语言之间用来交换数据;
  • 小型数据库:用来当数据库存储数据。

XML与HTML比较

  • HTML的元素都是固定的,而XML可以自定义元素;
  • HTML用浏览器来解析执行, XML的解析器通常需要自己来写(因为元素是自定义的);
  • HTML只能用来表示网页,而XML可以做的事情很多。

XML和properties(属性文件)比较

  • 属性文件只能存储平面信息,而XML可以存储结构化信息;
  • 解析属性文件只需要使用Properties类就可以了,而解析XML文档是很复杂的。

XML文档的组成部分

  • XML文档声明;重要
  • XML处理指令;看完了,就可以忘了!
  • XML元素;最重要
  • XML特殊字符和CDATA区;一看就会
  • XML注释。不看都会
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<students>
    <student number="1001">
        <name>zhangSan</name>
        <age>23</age>
        <sex>male</sex>
    </student>
    <student number="1002">
        <name>liSi</name>
        <age>32</age>
        <sex>female</sex>
    </student>
    <student number="1003">
        <name>wangWu</name>
        <age>55</age>
        <sex>male</sex>
    </student>
</students>

什么是xml文档声明

可以把xml文档声明看成是xml文档说明。
最简单的xml文档声明:< ?xml version=”1.0”? >
注意,XML是区别大小写,这一点不同与HTML!

xml文档声明结构

  • version属性
    用于说明当前xml文档的版本,因为都是在用1.0,所以这个属性值大家都写1.0,version属性是必须的;

  • encoding属性
    用于说明当前xml文档使用的字符编码集,xml解析器会使用这个编码来解析xml文档。encoding属性是可选的,默认为UTF-8。注意,如果当前xml文档使用的字符编码集是gb2312,而encoding属性的值为UTF-8,那么一定会出错的;

  • standalone属性
    用于说明当前xml文档是否为独立文档,如果该属性值为yes,表示当前xml文档是独立的,如果为no表示当前xml文档不是独立的,即依赖外部的文件。默认是yes

  • 没有xml文档声明的xml文档,不是格式良好的xml文档;

  • xml文档声明必须从xml文档的1行1列开始。

xml的中文乱码问题解决

保存时候的编码和设置打开时候的编码一致,不会出现乱码

Xml 格式数据的生成和解析

转义字符

因为在xml文档中有些字符是特殊的,不能使用它们作为文本数据。例如:不能使用“<”或“>”等字符作为文本数据,所以需要使用转义字符来表示。
例如<a><a></a>,你可能会说,其中第二个<a>是a元素的文本内容,而不是一个元素的开始标签,但xml解析器是不会明白你的意思的。
把<a><a></a>修饰为<a>&lt;a&gt;</a>,这就OK了。

Xml 格式数据的生成和解析

转义字符都是以“&”开头,以“;”结束。这与后面我们学习的实体是相同的。

CDATA区(CDATA段)

当大量的转义字符出现在xml文档中时,会使xml文档的可读性大幅度降低。这时如果使用CDATA段就会好一些。

在CDATA段中出现的“<”、“>”、“””、“’”、“&”,都无需使用转义字符。这可以提高xml文档的可读性

&lt;a>&lt;![CDATA[&lt;a>]]>&lt;/a>

在CDATA段中不能包含“]]>”,即CDATA段的结束定界符

XML实战案例

使用xml 作为数据交互的载体是Android 中非常重要的功能,比如天气预报数据、短信备份数据、通讯录数据都可以以xml 的格式通过网络传输。

为了演示Xml 数据的操作,我模拟了一个短信备份的案例。

需求:界面如下图所示。上面是三个Button,前两个分别对应两种不同方式生成xml,第三个Button点击后解析备份的xml 文件,然后将数据展现在下面的ScrollView 中。短信数据是模拟的假数据。

Xml 格式数据的生成和解析

生成的xml 格式如下

<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
    <smses>
      <sms>
        <address>5554</address>
        <body>我是内容<>0</body>
        <time>1445595309201</time>
      </sms>
      <sms>
        <address>5555</address>
        <body>我是内容<>1</body>
        <time>1445595309201</time>
        </sms>
 </smses>

编写布局文件

<?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">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="click1"
        android:text="生成xml1"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="click2"
        android:text="生成xml2"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="click3"
        android:text="解析xml"/>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/tv_sms"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </ScrollView>

</LinearLayout>

拼接字符串方式生成Xml 文件

        //第一种方式生成xml
        public void click1 (View view)throws Exception {
            StringBuilder sb = new StringBuilder();
            sb.append("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>");
            sb.append("<smses>");
            for (int i = 0; i < 50; i++) {
                sb.append("<sms>");
                sb.append("<address>");
                sb.append(5554 + i);
                sb.append("</address>");
                sb.append("<body>");
                sb.append("我是短信内容" + i);
                sb.append("</body>");
                sb.append("<time>");
                sb.append(new Date().getTime());
                sb.append("</time>");
                sb.append("</sms>");
            }
            sb.append("</smses>");

            //使用系统提供的方法获取文件输出流
            FileOutputStream fos = this.openFileOutput("info.xml", MODE_PRIVATE);
            fos.write(sb.toString().getBytes());
            fos.close();
        }

上面的代码虽然也可以生成xml 文件,但是无法对特殊字符进行处理,比如如果短信内容包含“

使用XmlSerializer 生成Xml 文件

        /**
         * 第二种方式生成xml
         * 使用Android 提供的API
         */
        public void click2 (View view)throws Exception {
            //在data 目录中创建info2.xml 文件,获取输出流
            FileOutputStream fos = openFileOutput("info2.xml", MODE_PRIVATE);
            //通过Xml 类创建一个Xml 序列化器
            XmlSerializer serializer = Xml.newSerializer();
            //给序列化器设置输出流和输出流编码
            serializer.setOutput(fos, "utf-8");
            /**
             * 让序列化器开发写入xml 的头信息,其本质是写入内容:
             * "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
             */
            serializer.startDocument("utf-8", true);
            /**
             * 开始写入smses 标签,
             * 第一个参数是该标签的命名空间, 这里不需要
             */
            serializer.startTag(null, "smses");
            for (int i = 0; i < 50; i++) {
                //开始sms 标签
                serializer.startTag(null, "sms");
                //开始address 标签
                serializer.startTag(null, "address");
                //在address 标签中间写入文本
                serializer.text("" + (5554 + i));
                //结束address 标签
                serializer.endTag(null, "address");
                //开始body 标签
                serializer.startTag(null, "body");
                //在body 标签中间写入文本
                serializer.text("我是内容<>" + i);
                //结束body 标签
                serializer.endTag(null, "body");
                serializer.startTag(null, "time");
                serializer.text(new Date().getTime() + "");
                serializer.endTag(null, "time");
                //结束sms 标签
                serializer.endTag(null, "sms");
            }
            //结束smses 标签
            serializer.endTag(null, "smses");
            //结束文档
            serializer.endDocument();
            fos.close();
        }

使用XmlSerializer 生成xml 文件是推荐的方式,因为该api 内部已经实现了对特殊字符的处理

使用Pull 解析Xml 格式数据

asserts 资源目录中的文件只能读不能写,多用于随apk 一起发布的固定不变的数据,比如可以将国内所有的城市列表放在里面。

这里将生成的info2.xml 放到asserts 目录中,然后读取、解析、展现

    //使用pull 解析xml 数据
    public void click3(View v) throws Exception {
        /**
         * 将解析出来的数据封装在Sms 中,然后保存到ArrayList 中
         * Sms 是自定义的一个JavaBean
         */
        ArrayList<Sms> smses = null;
        Sms sms = null;
        //调用父类提供的getAssets()方法获取AssertManager 对象
        AssetManager assetManager = getAssets();
        //使用assetManager 的open 方法直接获取输入流对象
        InputStream inputStream = assetManager.open("info2.xml");
        //通过Xml 的静态方法获取Xml 解析器
        XmlPullParser parser = Xml.newPullParser();
        //设置输入流和编码
        parser.setInput(inputStream, "utf-8");
        //获取事件类型
        int event = parser.next();
        //如果没有解析到文档的结尾,则循环解析
        while (event != XmlPullParser.END_DOCUMENT) {
            //获取当前解析到的标签名称
            String tagName = parser.getName();
            //如果是“开始标签”事件
            if (event == XmlPullParser.START_TAG) {
                //判断当前解析到的开始标签是哪个
                if ("smses".equals(tagName)) {
                    smses = new ArrayList<Sms>();
                } else if ("sms".equals(tagName)) {
                    sms = new Sms();
                } else if ("address".equals(tagName)) {
                    sms.setAddress(parser.nextText());
                } else if ("body".equals(tagName)) {
                    sms.setBody(parser.nextText());
                } else if ("time".equals(tagName)) {
                    sms.setTime(parser.nextText());
                }
                //如果是“结束标签”事件
            } else if (event == XmlPullParser.END_TAG) {
                if ("sms".equals(tagName)) {
                    //如果是sms 结尾,则将创建的sms 对象添加到集合中
                    smses.add(sms);
                }
            }
            //继续获取下一个事件
            event = parser.next();
        }
        inputStream.close();
        //将数据展示到界面
        showSms(smses);

    }

    /**
     * 将短信显示到TextView 中
     */
    private void showSms(ArrayList<Sms> smses) {
        StringBuilder sb = new StringBuilder();
        for (Sms s : smses) {
            sb.append(s.toString() + "\n");
        }
        tv_sms.setText(sb.toString());
    }

Pull 解析和SAX 解析对比

Pull 解析器的运行方式与SAX 解析器相似,都属于事件驱动模式。它提供了类似的事件,如:开始元素和结束元素事件,使用parser.next()可以进入下一个元素并触发相应事件。事件将作为数值代码被发送,因此可以使用一个switch 对感兴趣的事件进行处理。当元素开始解析时,调用parser.nextText()方法可以获取下一个Text 类型元素的值

SAX 解析器的工作方式是自动将事件推入事件处理器进行处理,因此你不能控制事件的处理主动结束;而Pull 解析器的工作方式为允许你的应用程序代码主动从解析器中获取事件,正因为是主动获取事件,因此可以在满足了需要的条件后不再获取事件,结束解析

简单新闻客户端

Xml 格式数据的生成和解析

新闻数据

<?xml version="1.0" encoding="UTF-8" ?>
<newslist>
    <news>
        <title>黑马52期就业快报</title>
        <detail>热烈祝贺黑马52期平均薪水突破13k</detail>
        <comment>15687</comment>
        <image>http://192.168.1.100:8080/images/6.jpg</image>
    </news>
    <news>
        <title>程序员因写代码太乱被杀害</title>
        <detail>凶手是死者同事,维护死者代码时完全看不懂而痛下杀手</detail>
        <comment>16359</comment>
        <image>http://192.168.1.100:8080/images/7.jpg</image>
    </news>
    <news>
        <title>产品经理因频繁改需求被杀害</title>
        <detail>凶手是一名程序员,因死者对项目需求频繁改动而痛下杀手</detail>
        <comment>14112</comment>
        <image>http://192.168.1.100:8080/images/7.jpg</image>
    </news>
    <news>
        <title>3Q大战宣判: 腾讯获赔500万</title>
        <detail>最高法驳回360上诉, 维持一审宣判.</detail>
        <comment>6427</comment>
        <image>http://192.168.1.100:8080/images/1.jpg</image>
    </news>
    <news>
        <title>今日之声:北大雕塑被戴口罩</title>
        <detail>市民: 因雾霾起诉环保局; 公务员谈"紧日子": 坚决不出去.</detail>
        <comment>681</comment>
        <image>http://192.168.1.100:8080/images/2.jpg</image>
    </news>
    <news>
        <title>奥巴马见达赖是装蒜</title>
        <detail>外文局: 国际民众认可中国大国地位;法院: "流量清零"未侵权.</detail>
        <comment>1359</comment>
        <image>http://192.168.1.100:8080/images/3.jpg</image>
    </news>
    <news>
        <title>轻松一刻: 我要沉迷学习不自拔</title>
        <detail>放假时我醒了不代表我起床了, 如今我起床了不代表我醒了!</detail>
        <comment>11616</comment>
        <image>http://192.168.1.100:8080/images/4.jpg</image>
    </news>
    <news>
        <title>男女那些事儿</title>
        <detail>"妈, 我在东莞被抓, 要2万保释金, 快汇钱到xxx!"</detail>
        <comment>10339</comment>
        <image>http://192.168.1.100:8080/images/5.jpg</image>
    </news>
    <news>
        <title>赵帅哥语录一</title>
        <detail>少壮不努力,老大做IT</detail>
        <comment>14612</comment>
        <image>http://192.168.1.100:8080/images/8.jpg</image>
    </news>
    <news>
        <title>赵帅哥语录二</title>
        <detail>问君能有几多愁,恰似调完代码改需求</detail>
        <comment>13230</comment>
        <image>http://192.168.1.100:8080/images/8.jpg</image>
    </news>
    <news>
        <title>赵帅哥语录三</title>
        <detail>觉得我帅的人工资一般都比较高</detail>
        <comment>9928</comment>
        <image>http://192.168.1.100:8080/images/8.jpg</image>
    </news>
    <news>
        <title>今日之声:北大雕塑被戴口罩</title>
        <detail>市民: 因雾霾起诉环保局; 公务员谈"紧日子": 坚决不出去.</detail>
        <comment>681</comment>
        <image>http://192.168.1.100:8080/images/2.jpg</image>
    </news>
    <news>
        <title>奥巴马见达赖是装蒜</title>
        <detail>外文局: 国际民众认可中国大国地位;法院: "流量清零"未侵权.</detail>
        <comment>1359</comment>
        <image>http://192.168.1.100:8080/images/3.jpg</image>
    </news>
</newslist>

布局文件

<RelativeLayout 
    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"
    tools:context=".MainActivity" >

    <ListView 
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

ListView的item布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <com.loopj.android.image.SmartImageView 
        android:id="@+id/iv"
        android:layout_width="90dp"
        android:layout_height="70dp"
        android:src="@drawable/ic_launcher"
        android:layout_centerVertical="true"/>
    <TextView 
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="这是大标题志哥教你上塑料"
        android:layout_toRightOf="@id/iv"
        android:textSize="22sp"
        android:singleLine="true"
        />
     <TextView 
         android:id="@+id/tv_detail"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="这是正文志哥教你带崩三路"
        android:layout_toRightOf="@id/iv"
        android:layout_below="@id/tv_title"
        android:textSize="15sp"
        android:textColor="@android:color/darker_gray"
        android:lines="2"
        />
      <TextView 
          android:id="@+id/tv_comment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="65031条评论"
        android:textColor="#ff0000"
        android:layout_alignParentRight="true"
        android:layout_below="@id/tv_detail"
        />
</RelativeLayout>

实体bean

public class News {

    private String title;
    private String detail;
    private String comment;
    private String imageUrl;

    @Override
    public String toString() {
        return "News [title=" + title + ", detail=" + detail + ", comment="
                + comment + ", imageUrl=" + imageUrl + "]";
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }
}

实现代码

public class MainActivity extends Activity {

    List<News> newsList;
    Handler handler = new Handler(){
        public void handleMessage(android.os.Message msg) {
            ListView lv = (ListView) findViewById(R.id.lv);
            lv.setAdapter(new MyAdapter());
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        getNewsInfo();
//      ListView lv = (ListView) findViewById(R.id.lv);
//      //要保证在设置适配器时,新闻xml文件已经解析完毕了
//      lv.setAdapter(new MyAdapter());
    }

    class MyAdapter extends BaseAdapter{

        //得到模型层中元素的数量,用来确定listview需要有多少个条目
        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return newsList.size();
        }
        @Override
        //返回一个View对象,作为listview的条目显示至界面
        public View getView(int position, View convertView, ViewGroup parent) {

            News news = newsList.get(position);
            View v = null;
            ViewHolder mHolder;
            if(convertView == null){
                v = View.inflate(MainActivity.this, R.layout.item_listview, null);
                mHolder = new ViewHolder();
                //把布局文件中所有组件的对象封装至ViewHolder对象中
                mHolder.tv_title = (TextView) v.findViewById(R.id.tv_title);
                mHolder.tv_detail = (TextView) v.findViewById(R.id.tv_detail);
                mHolder.tv_comment = (TextView) v.findViewById(R.id.tv_comment);
                mHolder.siv = (SmartImageView) v.findViewById(R.id.iv);
                //把ViewHolder对象封装至View对象中
                v.setTag(mHolder);
            }
            else{
                v = convertView;
                mHolder = (ViewHolder) v.getTag();
            }
            //给三个文本框设置内容
            mHolder.tv_title.setText(news.getTitle());

            mHolder.tv_detail.setText(news.getDetail());

            mHolder.tv_comment.setText(news.getComment() + "条评论");

            //给新闻图片imageview设置内容
            mHolder.siv.setImageUrl(news.getImageUrl());
            return v;
        }

        class ViewHolder{
            //条目的布局文件中有什么组件,这里就定义什么属性
            TextView tv_title;
            TextView tv_detail;
            TextView tv_comment;
            SmartImageView siv;
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }
    }
    private void getNewsInfo() {
        Thread t = new Thread(){
            @Override
            public void run() {
                String path = "http://192.168.13.13:8080/news.xml";
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setConnectTimeout(5000);
                    conn.setReadTimeout(5000);
                    //发送http GET请求,获取相应码
                    if(conn.getResponseCode() == 200){
                        InputStream is = conn.getInputStream();
                        //使用pull解析器,解析这个流
                        parseNewsXml(is);
                    }
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        };
        t.start();
    }

    private void parseNewsXml(InputStream is) {
        XmlPullParser xp = Xml.newPullParser();
        try {
            xp.setInput(is, "utf-8");
            //对节点的事件类型进行判断,就可以知道当前节点是什么节点
            int type = xp.getEventType();
            News news = null;
            while(type != XmlPullParser.END_DOCUMENT){
                switch (type) {
                case XmlPullParser.START_TAG:
                    if("newslist".equals(xp.getName())){
                        newsList = new ArrayList<News>();
                    }
                    else if("news".equals(xp.getName())){
                        news = new News();
                    }
                    else if("title".equals(xp.getName())){
                        String title = xp.nextText();
                        news.setTitle(title);
                    }
                    else if("detail".equals(xp.getName())){
                        String detail = xp.nextText();
                        news.setDetail(detail);
                    }
                    else if("comment".equals(xp.getName())){
                        String comment = xp.nextText();
                        news.setComment(comment);
                    }
                    else if("image".equals(xp.getName())){
                        String image = xp.nextText();
                        news.setImageUrl(image);
                    }
                    break;
                case XmlPullParser.END_TAG:
                    if("news".equals(xp.getName())){
                        newsList.add(news);
                    }
                    break;

                }
                //解析完当前节点后,把指针移动至下一个节点,并返回它的事件类型
                type = xp.next();
            }
            //发消息,让主线程设置listview的适配器,如果消息不需要携带数据,可以发送空消息
            handler.sendEmptyMessage(1);
//          for (News n : newsList) {
//              System.out.println(n.toString());
//          }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

Android视频教程

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值