• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
随—风
博客园    首页    新随笔    联系   管理    订阅  订阅

Android 中解析 XML

对于这三种解析器各有优点,我个人比较倾向于 PULL 解析器,因为 SAX 解析器操作起来太笨重,DOM 不适合文档较大,内存较小的场景,唯有 PULL 轻巧灵活,速度快,占用内存小,使用非常顺手。读者也可以根据自己的喜好选择相应的解析技术。

XML 在各种开发中都广泛应用,Android 也不例外。作为承载数据的一个重要角色,如何读写 XML 成为 Android 开发中一项重要的技能。今天就由我向大家介绍一下在 Android平台下几种常见的 XML 解析和创建的方法。

在 Android 中,常见的 XML 解析器分别为 SAX 解析器、DOM 解析器和 PULL 解析器,下面,我将一一向大家详细介绍。

SAX解析器:
SAX(Simple API for XML)解析器是一种基于事件的解析器,它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。
SAX解析器的优点是解析速度快,占用内存少。非常适合在Android移动设备中使用。

DOM解析器:
DOM是基于树形结构的的节点或信息片段的集合,允许开发人员使用DOM API遍历XML树、检索所需数据。分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。
由于DOM在内存中以树形结构存放,因此检索和更新效率会更高。但是对于特别大的文档,解析和加载整个文档将会很耗资源。

PULL解析器:
PULL解析器的运行方式和SAX类似,都是基于事件的模式。不同的是,在PULL解析过程中,我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码。PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android移动设备中使用,Android系统内部在解析各种XML时也是用PULL解析器。
以上三种解析器,都是非常实用的解析器,我将会一一介绍。我们将会使用这三种解析技术完成一项共同的任务。

我们新建一个项目,项目结构如下:

 


 



我会在项目的 assets 目录中放置一个 XML 文档 books.xml,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<books>
    <book>
        <id>1001</id>
        <name>Thinking In Java</name>
        <price>80.00</price>
    </book>
    <book>
        <id>1002</id>
        <name>Core Java</name>
        <price>90.00</price>
    </book>
    <book>
        <id>1003</id>
        <name>Hello, Andriod</name>
        <price>100.00</price>
    </book>
</books>

然后我们分别使用以上三种解析技术解析文档,得到一个List<Book>的对象,先来看一下Book.java的代码:

public class Book {
    
    private int id;
    private String name;
    private float price;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "id:" + id + ", name:" + name + ", price:" + price;
    }
}

最后,我们还要把这个集合对象中的数据生成一个新的XML文档,如图:



生成的XML结构跟原始文档略有不同,是下面这种格式:

接下来,就该介绍操作过程了,我们先为解析器定义一个 BookParser 接口,每种类型的解析器需要实现此接口。BookParser.java代码如下:

public interface BookParser {

    /**
     * 解析输入流 得到 Book 对象集合
     * 
     * @param is
     * @return
     * @throws Exception
     */
    public List<Book> parse(InputStream is) throws Exception;

    /**
     * 序列化 Book 对象集合 得到 XML 形式的字符串
     * 
     * @param books
     * @return
     * @throws Exception
     */
    public String serialize(List<Book> books) throws Exception;
}

好了,我们就该一个一个的实现该接口,完成我们的解析过程。
使用SAX解析器:
SaxBookParser.java代码如下:

public class SaxBookParser implements BookParser {

    @Override
    public List<Book> parse(InputStream is) throws Exception {
        
        SAXParserFactory tFactory = SAXParserFactory.newInstance(); // 取得SAXParserFactory实例
        
        SAXParser parser = tFactory.newSAXParser(); // 从factory获取SAXParser实例
        
        MyHandler tHandler = new MyHandler(); // 实例化自定义Handler
        
        parser.parse(is, tHandler); // 根据自定义Handler规则解析输入流
        
        return tHandler.getBooks();
    }

    @Override
    public String serialize(List<Book> books) throws Exception {
        
        SAXTransformerFactory tFactory = (SAXTransformerFactory) TransformerFactory
                .newInstance();// 取得SAXTransformerFactory实例
        
        TransformerHandler tHandler = tFactory.newTransformerHandler(); // 从factory获取TransformerHandler实例
        Transformer tTransformer = tHandler.getTransformer(); // 从handler获取Transformer实例
        tTransformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); // 设置输出采用的编码方式
        tTransformer.setOutputProperty(OutputKeys.INDENT, "yes"); // 是否自动添加额外的空白
        tTransformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); // 是否忽略XML声明

        StringWriter tWriter = new StringWriter();
        Result tResult = new StreamResult(tWriter);
        tHandler.setResult(tResult);

        String tUri = ""; // 代表命名空间的URI 当URI无值时 须置为空字符串
        String tLocalName = ""; // 命名空间的本地名称(不包含前缀) 当没有进行命名空间处理时 须置为空字符串

        tHandler.startDocument();
        tHandler.startElement(tUri, tLocalName, "books", null);

        AttributesImpl tAttrs = new AttributesImpl(); // 负责存放元素的属性信息
        
        char[] ch = null;
        
        for (Book book : books) {
            tAttrs.clear(); // 清空属性列表
            tAttrs.addAttribute(tUri, tLocalName, "id", "string",
                    String.valueOf(book.getId()));// 添加一个名为id的属性(type影响不大,这里设为string)
            tHandler.startElement(tUri, tLocalName, "book", tAttrs); // 开始一个book元素
                                                                 // 关联上面设定的id属性

            tHandler.startElement(tUri, tLocalName, "name", null); // 开始一个name元素
                                                                // 没有属性
            ch = String.valueOf(book.getName()).toCharArray();
            tHandler.characters(ch, 0, ch.length); // 设置name元素的文本节点
            tHandler.endElement(tUri, tLocalName, "name");

            tHandler.startElement(tUri, tLocalName, "price", null);// 开始一个price元素
                                                                // 没有属性
            ch = String.valueOf(book.getPrice()).toCharArray();
            tHandler.characters(ch, 0, ch.length); // 设置price元素的文本节点
            tHandler.endElement(tUri, tLocalName, "price");

            tHandler.endElement(tUri, tLocalName, "book");
        }
        tHandler.endElement(tUri, tLocalName, "books");
        tHandler.endDocument();

        return tWriter.toString();
    }

    // 需要重写DefaultHandler的方法
    private class MyHandler extends DefaultHandler {

        private List<Book> mBooks;
        private Book mBook;
        private StringBuilder mBuilder;

        // 返回解析后得到的Book对象集合
        public List<Book> getBooks() {

            return mBooks;
        }

        @Override
        public void startDocument() throws SAXException {
            super.startDocument();

            mBooks = new ArrayList<Book>();
            mBuilder = new StringBuilder();
        }

        @Override
        public void startElement(String uri, String localName, String qName,
                Attributes attributes) throws SAXException {

            super.startElement(uri, localName, qName, attributes);

            if (localName.equals("book")) {

                mBook = new Book();
            }

            mBuilder.setLength(0); // 将字符长度设置为0 以便重新开始读取元素内的字符节点
        }

        @Override
        public void characters(char[] ch, int start, int length)
                throws SAXException {
            super.characters(ch, start, length);

            mBuilder.append(ch, start, length); // 将读取的字符数组追加到builder中
        }

        @Override
        public void endElement(String uri, String localName, String qName)
                throws SAXException {
            super.endElement(uri, localName, qName);

            if (localName.equals("id")) {

                mBook.setId(Integer.parseInt(mBuilder.toString()));
            } else if (localName.equals("name")) {

                mBook.setName(mBuilder.toString());
            } else if (localName.equals("price")) {

                mBook.setPrice(Float.parseFloat(mBuilder.toString()));
            } else if (localName.equals("book")) {

                mBooks.add(mBook);
            }
        }
    }
}

代码中,我们定义了自己的事件处理逻辑,重写了 DefaultHandler 的几个重要的事件方法。下面我为大家着重介绍一下DefaultHandler的相关知识。DefaultHandler是一个事件处理器,可以接收解析器报告的所有事件,处理所发现的数据。它实现了EntityResolver接口、DTDHandler接口、ErrorHandler接口和ContentHandler接口。这几个接口代表不同类型的事件处理器。我们着重介绍一下ContentHandler接口。结构如图:

 

 



这几个比较重要的方法已被我用红线标注,DefaultHandler实现了这些方法,但在方法体内没有做任何事情,因此我们在使用时必须覆写相关的方法。最重要的是startElement方法、characters方法和endElement方法。当执行文档时遇到起始节点,startElement方法将会被调用,我们可以获取起始节点相关信息;然后characters方法被调用,我们可以获取节点内的文本信息;最后endElement方法被调用,我们可以做收尾的相关操作。
最后,我们需要调用SAX解析程序,这个步骤在MainActivity中完成:


使用DOM解析器:
DomBookParser.java代码如下:

public class DomBookParser implements BookParser {

    @Override
    public List<Book> parse(InputStream is) throws Exception {

        List<Book> tBooks = new ArrayList<Book>();

        DocumentBuilderFactory tFactory = DocumentBuilderFactory.newInstance(); // 取得
                                                                                // DocumentBuilderFactory
        DocumentBuilder tBuilder = tFactory.newDocumentBuilder(); // 从 factory
                                                                  // 获取
                                                                  // DocumentBuilder
        Document tDocument = tBuilder.parse(is); // 解析输入流 得到 Document 实例

        Element tRootElement = tDocument.getDocumentElement();
        NodeList tItems = tRootElement.getElementsByTagName("book");

        for (int i = 0; i < tItems.getLength(); i++) {

            Book tBook = new Book();
            Node tItem = tItems.item(i);
            NodeList tProperties = tItem.getChildNodes();

            for (int j = 0; j < tProperties.getLength(); j++) {

                Node tProperty = tProperties.item(j);
                String tNodeName = tProperty.getNodeName();

                if (tNodeName.equals("id")) {

                    tBook.setId(Integer.parseInt(tProperty.getFirstChild()
                            .getNodeValue()));
                } else if (tNodeName.equals("name")) {

                    tBook.setName(tProperty.getFirstChild().getNodeValue());
                } else if (tNodeName.equals("price")) {

                    tBook.setPrice(Float.parseFloat(tProperty.getFirstChild()
                            .getNodeValue()));
                }
            }

            tBooks.add(tBook);
        }
        return tBooks;
    }

    @Override
    public String serialize(List<Book> books) throws Exception {

        DocumentBuilderFactory tFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder tBuilder = tFactory.newDocumentBuilder();
        Document tDocument = tBuilder.newDocument(); // 由builder创建新文档

        Element tRootElement = tDocument.createElement("books");

        for (Book book : books) {

            Element tBookElement = tDocument.createElement("book");
            tBookElement.setAttribute("id", book.getId() + "");

            Element tNameElement = tDocument.createElement("name");
            tNameElement.setTextContent(book.getName());
            tBookElement.appendChild(tNameElement);

            Element tPriceElement = tDocument.createElement("price");
            tPriceElement.setTextContent(book.getPrice() + "");
            tBookElement.appendChild(tPriceElement);

            tRootElement.appendChild(tBookElement);
        }

        tDocument.appendChild(tRootElement);

        TransformerFactory rTransFactory = TransformerFactory.newInstance();// 取得TransformerFactory实例
        Transformer tTransformer = rTransFactory.newTransformer(); // 从transFactory获取Transformer实例
        tTransformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); // 设置输出采用的编码方式
        tTransformer.setOutputProperty(OutputKeys.INDENT, "yes"); // 是否自动添加额外的空白
        tTransformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); // 是否忽略XML声明

        StringWriter tWriter = new StringWriter();

        Source tSource = new DOMSource(tDocument); // 表明文档来源是doc
        Result tResult = new StreamResult(tWriter);// 表明目标结果为writer
        tTransformer.transform(tSource, tResult); // 开始转换

        return tWriter.toString();
    }
}



使用PULL解析器:
PullBookParser.java代码如下:

public class PullBookParser implements BookParser {

    @Override
    public List<Book> parse(InputStream is) throws Exception {

        List<Book> tBooks = null;
        Book tBook = null;

        // XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        // XmlPullParser parser = factory.newPullParser();

        XmlPullParser tParser = Xml.newPullParser(); // 由android.util.Xml创建一个XmlPullParser实例
        tParser.setInput(is, "UTF-8"); // 设置输入流 并指明编码方式

        int tEventType = tParser.getEventType();

        while (tEventType != XmlPullParser.END_DOCUMENT) {

            switch (tEventType) {

            case XmlPullParser.START_DOCUMENT:

                tBooks = new ArrayList<Book>();
                break;

            case XmlPullParser.START_TAG:

                if (tParser.getName().equals("book")) {

                    tBook = new Book();
                } else if (tParser.getName().equals("id")) {

                    tEventType = tParser.next();
                    tBook.setId(Integer.parseInt(tParser.getText()));
                } else if (tParser.getName().equals("name")) {

                    tEventType = tParser.next();
                    tBook.setName(tParser.getText());
                } else if (tParser.getName().equals("price")) {

                    tEventType = tParser.next();
                    tBook.setPrice(Float.parseFloat(tParser.getText()));
                }
                break;
            case XmlPullParser.END_TAG:

                if (tParser.getName().equals("book")) {

                    tBooks.add(tBook);
                    tBook = null;
                }
                break;
            }

            tEventType = tParser.next();
        }
        return tBooks;
    }

    @Override
    public String serialize(List<Book> books) throws Exception {

        // XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        // XmlSerializer serializer = factory.newSerializer();

        XmlSerializer tSerializer = Xml.newSerializer(); // 由android.util.Xml创建一个XmlSerializer实例
        StringWriter tWriter = new StringWriter();

        tSerializer.setOutput(tWriter); // 设置输出方向为writer
        tSerializer.startDocument("UTF-8", true);
        tSerializer.startTag("", "books");

        for (Book book : books) {

            tSerializer.startTag("", "book");
            tSerializer.attribute("", "id", book.getId() + "");

            tSerializer.startTag("", "name");
            tSerializer.text(book.getName());
            tSerializer.endTag("", "name");

            tSerializer.startTag("", "price");
            tSerializer.text(book.getPrice() + "");
            tSerializer.endTag("", "price");

            tSerializer.endTag("", "book");
        }

        tSerializer.endTag("", "books");
        tSerializer.endDocument();

        return tWriter.toString();
    }
}

最后我们看 MainActivity 方法
public class MainActivity extends Activity implements OnClickListener {

    private static final String TAG = "XML";

    private BookParser mBookParser;
    private List<Book> mBooks;

    private TextView mTvXml;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        initControl();
    }

    /**
     * 加载控件
     */
    private void initControl() {

        setContentView(R.layout.activity_main);

        mTvXml = (TextView) this.findViewById(R.id.tvXml);

        Button tBtnSaxRead = (Button) this.findViewById(R.id.btnSaxRead);
        Button tBtnSaxWrite = (Button) this.findViewById(R.id.btnSaxWrite);

        Button tBtnDomRead = (Button) this.findViewById(R.id.btnDomRead);
        Button tBtnDomWrite = (Button) this.findViewById(R.id.btnDomWrite);

        Button tBtnPullRead = (Button) this.findViewById(R.id.btnPullRead);
        Button tBtnPullWrite = (Button) this.findViewById(R.id.btnPullWrite);

        tBtnSaxRead.setOnClickListener(this);
        tBtnSaxWrite.setOnClickListener(this);

        tBtnDomRead.setOnClickListener(this);
        tBtnDomWrite.setOnClickListener(this);

        tBtnPullRead.setOnClickListener(this);
        tBtnPullWrite.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {

        switch (v.getId()) {
        case R.id.btnSaxRead: // Sax读取

            mBookParser = new SaxBookParser(); // 创建SaxBookParser实例
            read("Sax读取:");

            break;

        case R.id.btnSaxWrite:

            mBookParser = new SaxBookParser(); // 创建SaxBookParser实例
            write();

            break;
        case R.id.btnDomRead:

            mBookParser = new DomBookParser(); // 创建DomBookParser实例
            read("Dom读取:");

            break;
        case R.id.btnDomWrite:

            mBookParser = new DomBookParser(); // 创建DomBookParser实例
            write();

            break;
        case R.id.btnPullRead:

            mBookParser = new PullBookParser(); // 创建PullBookParser实例
            read("Pull读取:");

            break;
        case R.id.btnPullWrite:

            mBookParser = new PullBookParser(); // 创建PullBookParser实例
            write();

            break;
        }
    }

    /**
     * 读取
     */
    private void read(String title) {

        StringBuffer tXmlContent = new StringBuffer();
        tXmlContent.append(title);
        try {

            InputStream tInputStream = getAssets().open("books.xml");
            mBooks = mBookParser.parse(tInputStream); // 解析输入流

            for (Book book : mBooks) {

                Log.i(TAG, book.toString());
                tXmlContent.append(book.toString());
                tXmlContent.append("—");
            }

            mTvXml.setText(tXmlContent.toString());
        } catch (Exception e) {

            Log.e(TAG, e.getMessage());
        }
    }

    /**
     * 写入
     */
    private void write() {

        try {

            String xml = mBookParser.serialize(mBooks); // 序列化

            FileOutputStream tFileOutputStream = openFileOutput("books.xml",
                    Context.MODE_PRIVATE);

            tFileOutputStream.write(xml.getBytes("UTF-8"));

        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }
}

【运行结果】

 

posted @ 2019-11-11 16:21  随—风  阅读(285)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3