XML简介及解析

一、XML简介:

XML即可扩展标记语言(eXtensible Markup Language)。XML是一种标记语言,很类似HTML,被设计用来结构化、存储以及传输信息,其设计宗旨是传输数据,而非显示数据,被设计为具有自我描述性。XML标签没有被预定义,需要自行定义标签。XML是W3C的推荐标准。

 XML允许创作者定义自己的标签和自己的文档结构。

XML语法:

1.      所有元素都必须有关闭标签

2.      XML标签对大小写敏感

3.      元素必须正确嵌套

4.      XML文档必须有根元素

<root>

  <child>

    <subchild>.....</subchild>

  </child>

</root>

5.      XML属性值必须加引号

6.      XML中的注释语法与HTML很相似

<!—这是一条注释 -- >

二、XML解析

XML有三种解析方式---SAX、DOM、PULL

SAX解析器

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

SAX的工作原理:SAX的工作原理简单地说就是对文档进行顺序扫描,

当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。

 在SAX接口中,事件源是org.xml.sax包中的XMLReader,它通过parser()方法来解析XML文档,并产生事件。

事件处理器是org.xml.sax包中ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口。XMLReader通过相应事件处理器注册方法setXXXX()来完成的与ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口的连接。

常用的SAX接口和类

 Attrbutes:用于得到属性的个数、名字和值。 

 ContentHandler:定义与文档本身关联的事件(例如,开始和结束标记)。大多数应用程序都注册这些事件。

 DTDHandler:定义与DTD关联的事件。它没有定义足够的事件来完整地报告DTD。如果需要对DTD进行语法分析,请使用可选的DeclHandler。

 DeclHandler是SAX的扩展。不是所有的语法分析器都支持它。

 EntityResolver:定义与装入实体关联的事件。只有少数几个应用程序注册这些事件。

 ErrorHandler:定义错误事件。许多应用程序注册这些事件以便用它们自己的方式报错。

 DefaultHandler:它提供了这些接LI的缺省实现。在大多数情况下,为应用程序扩展DefaultHandler并覆盖相关的方法要比直接实现一个接口更容易。



DOM解析器

DOM(DocumentObject Model)是基于树形结构的的节点或信息片段的集合,能够通过DOM树来访问所有元素。可以修改或删除它们的内容,并创建新的元素。分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。DOM将XML文档作为一个树形结构,而树叶被定义为节点。Android完全支持DOM 解析。利用DOM中的对象,可以对XML文档进行读取、搜索、修改、添加和删除等操作。

DOM的工作原理:使用DOM对XML文件进行操作时,首先要解析文件,将文件分为独立的元素、属性和注释等,然后以节点树的形式在内存中对XML文件进行表示,就可以通过节点树访问文档的内容,并根据需要修改文档——这就是DOM的工作原理。DOM实现时首先为XML文档的解析定义一组接口,解析器读入整个文档,然后构造一个驻留内存的树结构,这样代码就可以使用DOM接口来操作整个树结构。由于DOM在内存中以树形结构存放,因此检索和更新效率会更高。但是对于特别大的文档,解析和加载整个文档将会很耗资源。

常用的DoM接口和类:

 Document:该接口定义分析并创建DOM文档的一系列方法,它是文档树的根,是操作DOM的基础。 

Element:该接口继承Node接口,提供了获取、修改XML元素名字和属性的方法。

Node:该接口提供处理并获取节点和子节点值的方法。

NodeList:提供获得节点个数和当前节点的方法。这样就可以迭代地访问各个节点。

DOMParser:该类是Apache的Xerces中的DOM解析器类,可直接解析XML文件。


PULL解析器

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

PULL的工作原理:XML pull提供了开始元素和结束元素。当某个元素开始时,我们可以调用parser.nextText从XML文档中提取所有字符数据。当解释到一个文档结束时,自动生成EndDocument事件。

常用的XML Pull的接口和类

XmlPullParser:XML pull解析器是一个在XMLPULLVlAP1中提供了定义解析功能的接口。

XmlSerializer:它是一个接口,定义了XML信息集的序列。

XmlPullParserFactory:这个类用于在XMPULLV1 API中创建XML Pull解析器。

XmlPullParserException:抛出单一的XMLpull解析器相关的错误。


下面是代码示例:

首先在项目中新建一个XML文档

<?xml version="1.0" encoding="utf-8"?>
<books>
    <book>
        <id>
1001</id>
        <name>
Crazy Java</name>
        <price>
80.00</price>
    </book>
    <book>
        <id>
1002</id>
        <name>
Find Java</name>
        <price>
90.00</price>
    </book>
    <book>
        <id>
1003</id>
        <name>
Hello Android </name>
        <price>
100.00</price>
    </book>
</books>

定义一个BookParser接口,分别用SAX、DOM、PULL三种方法实现该接口

public interface BookParser {
    
     // 解析输入流,得到book对象集合
   
    public List<Book> parser(InputStream is) throws Exception;

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

定义一个Bean 存放解析得来的XML

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;
    }
}

下面是三个实现方法

1.DOM解析

public class DomBookParser implements BookParser {

    @Override
    public List<Book> parser(InputStream is) throws Exception {
        List<Book> books = new ArrayList<Book>();
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance()//取得DocumentBuilderFactory实例
        DocumentBuilder builder = factory.newDocumentBuilder(); //从factory获取DocumentBuilder实例
        Document doc = builder.parse(is);   //解析输入流 得到Document实例
        Element rootElement = doc.getDocumentElement();
        NodeList items = rootElement.getElementsByTagName("book");
        for (int i = 0; i < items.getLength(); i++) {
            Book book = new Book();
            Node item = items.item(i);
            NodeList properties = item.getChildNodes();
            for (int j = 0; j < properties.getLength(); j++) {
                Node property = properties.item(j);
                String nodeName = property.getNodeName();
                if (nodeName.equals("id")) {
                    book.setId(Integer.parseInt(property.getFirstChild().getNodeValue()));
                } else if (nodeName.equals("name")) {
                    book.setName(property.getFirstChild().getNodeValue());
                } else if (nodeName.equals("price")) {
                    book.setPrice(Float.parseFloat(property.getFirstChild().getNodeValue()));
                }
            }
            books.add(book);
        }
        return books;
    }


    @Override
    public String serialize(List<Book> books) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.newDocument();   //由builder创建新文档

        Element rootElement = doc.createElement("books");

        for (Book book : books) {
            Element bookElement = doc.createElement("book");
            bookElement.setAttribute("id", book.getId() + "");

            Element nameElement = doc.createElement("name");
            nameElement.setTextContent(book.getName());
            bookElement.appendChild(nameElement);

            Element priceElement = doc.createElement("price");
            priceElement.setTextContent(book.getPrice() + "");
            bookElement.appendChild(priceElement);

            rootElement.appendChild(bookElement);
        }

        doc.appendChild(rootElement);

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


        StringWriter writer = new StringWriter();

        Source source = new DOMSource(doc); //表明文档来源是doc
        Result result = new StreamResult(writer);//表明目标结果为writer
        transformer.transform(source, result)//开始转换

        return writer.toString();
    }
}

2.SAX解析

public class SaxBookParser implements BookParser{

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

        SAXParserFactory factory = SAXParserFactory.newInstance()//取得SAXParserFactory实例
        SAXParser parser = factory.newSAXParser();                  //从factory获取SAXParser实例
        MyHandler handler = new MyHandler();                        //实例化自定义Handler
        parser.parse(is, handler);                                  //根据自定义Handler规则解析输入流
        return handler.getBooks();

    }

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

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

        StringWriter writer = new StringWriter();
        Result result = new StreamResult(writer);


        handler.setResult(result);

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

        handler.startDocument();
        handler.startElement(uri, localName, "books", null);

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

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

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

            handler.endElement(uri, localName, "book");
        }
        handler.endElement(uri, localName, "books");
        handler.endDocument();

        return writer.toString();
    }

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

        private List<Book> books;
        private Book book;
        private StringBuilder builder;

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

        @Override
        public void startDocument() throws SAXException {
            super.startDocument();
            books = new ArrayList<Book>();
            builder = 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")) {
                book = new Book();
            }
            builder.setLength(0);   //将字符长度设置为0 以便重新开始读取元素内的字符节点
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            super.characters(ch, start, length);
            builder.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")) {
                book.setId(Integer.parseInt(builder.toString()));
            } else if (localName.equals("name")) {
                book.setName(builder.toString());
            } else if (localName.equals("price")) {
                book.setPrice(Float.parseFloat(builder.toString()));
            } else if (localName.equals("book")) {
                books.add(book);
            }
        }
    }
}

3.Pull解析

public class PullBookParser implements BookParser {
    @Override
    public List<Book> parser(InputStream is) throws Exception {
        List<Book> books = null;
        Book book = null;

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

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

        int eventType = parser.getEventType();
        while (eventType != XmlPullParser.END_DOCUMENT) {
            switch (eventType) {
                case XmlPullParser.START_DOCUMENT:
                    books = new ArrayList<Book>();
                    break;
                case XmlPullParser.START_TAG:
                    if (parser.getName().equals("book")) {
                        book = new Book();
                    } else if (parser.getName().equals("id")) {
                        eventType = parser.next();
                        book.setId(Integer.parseInt(parser.getText()));
                    } else if (parser.getName().equals("name")) {
                        eventType = parser.next();
                        book.setName(parser.getText());
                    } else if (parser.getName().equals("price")) {
                        eventType = parser.next();
                        book.setPrice(Float.parseFloat(parser.getText()));
                    }
                    break;
                case XmlPullParser.END_TAG:
                    if (parser.getName().equals("book")) {
                        books.add(book);
                        book = null;
                    }
                    break;
            }
            eventType = parser.next();
        }
        return books;
    }

    @Override
    public String serialize(List<Book> books) throws Exception {
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
//      XmlSerializer serializer = factory.newSerializer();

        XmlSerializer serializer = Xml.newSerializer(); //由android.util.Xml创建一个XmlSerializer实例
        StringWriter writer = new StringWriter();
        serializer.setOutput(writer);   //设置输出方向为writer
        serializer.startDocument("UTF-8", true);
        serializer.startTag("", "books");
        for (Book book : books) {
            serializer.startTag("", "book");
            serializer.attribute("", "id", book.getId() + "");

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

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

            serializer.endTag("", "book");
        }
        serializer.endTag("", "books");
        serializer.endDocument();

        return writer.toString();
    }
}

下面是MainActivity.java

public class MainActivity extends AppCompatActivity {


    private static final String TAG = "XML";

    private BookParser parser;
    private List<Book> books;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //
        Button readBtn = (Button) findViewById(R.id.readBtn);
        Button writeBtn = (Button) findViewById(R.id.writeBtn);

        readBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {


                try {
                    InputStream is = getAssets().open("books.xml");
                    //parser = new SaxBookParser();  //SAXParser
                    //parser = new DomBookParser(); //domparser
                    parser = new PullBookParser();//PullParser
                    books = parser.parser(is);
                    for (Book book : books) {
                        Log.i(TAG, book.toString());
                    }
                } catch (Exception e) {
                    Log.e(TAG, e.getMessage());
                }
            }

        });
        writeBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    String xml = parser.serialize(books)//序列化
                    FileOutputStream fos = openFileOutput("books.xml", Context.MODE_PRIVATE);
                    fos.write(xml.getBytes("UTF-8"));
                } catch (Exception e) {
                    Log.e(TAG, e.getMessage());
                }
            }
        });


    }

  
}

三种方法实现的效果是一样样的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值