xml解析(读取xml,保存文件到xml)

时间:2020-12-29 12:35:09

在Android中,实现对XML的解析有三种方式,分别为DOM解析器,SAX解析器和PULL解析器。


  • DOM解析器

DOM是基于树形结构的节点或信息片段的集合,允许开发人员使用DOM API遍历XML树,检索所需数据。

分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。

Android完全支持DOM解析。利用DOM中的对象,可以对XML文档进行读取,搜索,修改,添加和删除等操作。


DOM的工作原理:使用DOM对XML文件进行操作时,首先要解析文件,将文件分为独立的元素,属性和注释等,

然后以节点树的形式在内存中对XML文件进行表示,就可以通过节点树访问文档的内容,并根据需要修改文档。


DOM实现时首先为XML文档的解析定义一组接口,解析器读入整个文档,然后构造一个驻留内存的树结构,

这样代码就可以使用DOM接口来操作整个树结构。


由于DOM在内存中以树形结构存放,因此检索和更新效率会更高。但是对于特别大的文档,解析和加载整个文档将会

很耗资源。当然,如果XML文件的内容比较小,采用DOM是可行的。

常用的DOM接口和类:

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

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

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

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

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


示例:

    <?xml version="1.0" encoding="UTF-8"?>  
<persons>
<person id="1">
<name>叶问</name>
<age>23</age>
</person>
<person id="2">
<name>李小龙</name>
<age>17</age>
</person>
</persons>

public class DomXMLReader {

public static List<Person> readXML( InputStream inStream) {
List<Person> persons = new ArrayList<Person>;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builer = factory.newDocumentBuilder();
Document dom = builder.parse(inStream);
Element root = dom.getDocumentElement();
  NodeList items = root.getElementsByTagName("person");
             //查找所有person节点
            for (int i = 0; i < items.getLength(); i++) {
                      Person person = new Person();
                      //得到第一个person节点
                      Element personNode = (Element) items.item(i);
                      //获取person节点的id属性值
                      person.setId(new Integer(personNode.getAttribute("id")));
                      //获取person节点下的所有子节点(标签之间的空白节点和name/age元素)
                      NodeList childsNodes = personNode.getChildNodews();
                      for( int j =0; j < childsNodes.getLength(); j++) {
                        Node node = (Node) childsNodes.item(j);
                        //判断是否为元素类型
                        if( node.getNodeType() == Node.ELEMENT_NODE) {
                                Element childNode = (Element) node;
                                if( "name".equals(childNode.getNodeName())) {
                                    //获取name元素下Text节点,然后从Text节点获取数据
                                    person.setName( childNode.getFirstChild().getNodeValue());
                                } else if ("age".equals(childNode.getNodeName())) {
                                    person.setAge( new Short(childNode.getFirstChild().getNodeValue()));
                                }
                             }
                         }
                     persons.add(person);
            }
            inStream.close();
         } catch (Exception e) {
            e.printStackTrace();
         }
         return persons;

  • SAX解析器:

SAX解析器是一种基于事件的解析器,事件驱动的流式解析方式是,从文件的开始顺序解析到文档结束,不可暂停或倒退。

它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,

一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器

才能根据提供的事件信息来决定自己的行为。


SAX解析器的优点是解析速度快,占用内存少。非常适合在Android移动设备中使用。

SAX的工作原理: 对文档进行顺序扫描,当扫描到文档(document)开始与结束,元素(element)开始与结束,文档(document)结束等

地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描直到文档结束。


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

事件处理器是org.xml.sax包中ContentHandler, DTDHandler, ErrorHandler,以及EntityResolver这4个接口。

XMLReader通过相应事件处理器注册方法setXXXX()来完成的与ContentHandler, DTDHandler, ErrorHandler,

以及EntivityResolver这4个接口的连接。

常用的SAX接口和类:

         Attributes: 用于得到属性的个数,名字和值。

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

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

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

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

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

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


xml解析(读取xml,保存文件到xml)


示例:

<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person id="23">
<name>李明</name>
<age>30</age>
</person>
<person id="20">
<name>李向梅</name>
<age>25</age>
</person>
</persons>

public class Person implements Serializable{
private Integer id;
private String name;
private Short age;

public Person() {
}

public Person(String name, Short age) {
super();
this.name = name;
this.age = age;
}

public Integer getId() {
return id;
}

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

public String getName() {
return name;
}

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

public Short getAge() {
return age;
}

public void setAge(Short age) {
this.age = age;
}

}


public class XMLContenteHandler extends DefaultHandler {
   <span style="color:#000000;">  //临时变量,解决sax读取XML时不能读取换行符号后面的内容的问题;注意temp=""不能写成temp或temp=null,避免脏数据</span>
     private String temp="";
     private String preTag;
     private Person person;
     private List<Person> persons;

     public void startDocument() throws SAXException {
        persons = new ArrayList<Person>();
     }

     public void characters(char[] ch, int start, int length) throws SAXException {
        if(person != null) {
             String date = new String(ch, start, length);
             if("name".equals(preTag)){
               temp += date;
             } else if("age".equals(preTag)){
               temp += date;
             }
        }
     }

     public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
         if("person".equals(localName)){
            person = new Person();
            person.setId(attributes.getIndex("id"));
         }
         preTag = localName;
      }

      public void endElement( String uri, String localName, String name) throws SAXException {
         if(person != null && "name".equals(localName)) {
           person.setName(temp);
           temp="";
         } else if(person != null && "age".equals(localName)){
           person.setAge(new Short(temp));
           temp="";
         } else if(person != null && "person".equals(localName)) {
           persons.add(person);
           person = null;
         }
         preTag =null;
       }

       public List<person> getPersons() {
          return persons;
       }
}

public class SaxPersonService {
   // 业务层要对外抛出
   public static List<Person> readXML(InputStrean inStrean) throws Exception {
       
      SAXParserFactory spf = SAXParserFactory.newInstance();
      SAXParser saxParser = spf.newSAXParser();//创建解析器
      //设置解析器的相关特性,http://xnl.org/sax/features/namespaces = true;
      //表示开启命名空间特性
      //saxParser.setProperty("http://xnl.org/sax/features/namespaces", true);
      XMLContentHandler handler = new XMLContentHandler();
      saxParser.parse(inStream, handler);
      inStream.close();
      return handler.getPersons();
   }
}

public class SaxPersonServiceTest extends AndroidTestCase{
private final String TAG = "SaxPersonServiceTest";
public void testReadXML() throws Exception{
//ljq.xml放在src目录下
InputStream inputStream = SaxPersonServiceTest.class.getClassLoader().getResourceAsStream("ljq.xml");
List<Person> persons = SaxPersonService.readXML(inputStream);
for(Person person : persons){
Log.i(TAG, person.getId() + " : " + person.getName() + " : " + person.getAge());
}
}
}

  • PULL解析器

PULL解析器的运行方式和SAX类似,都是基于事件的模式。不同的是,在PULL解析过程中返回的是数字,且我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码。

PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android移动设备中使用,Android系统内部在解析各种XML时也是用PULL解析器,Android官方推荐开发者们使用Pull解析技术。Pull解析技术是第三方开发的开源技术,它同样可以应用于JavaSE开发。

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


读取XML

XmlPullParser pullParser = Xml.newPullParser();

pullParser.setInput(xml, "UTF-8"); // 为破解器添加要解析的xml数据

int event = pullParser.getEventType(); //开始读取,获取事件返回值

pullParser.getName(); // 获取节点名称

pullParser.getAttributeValue(0); // 获取第一个属性的值

pullParser.nextText(); //获取标签之后的节点内容

event = pullParser.next(); // 解析器遇到结束标签不会自动向下解析,需要调用此方法进行继续执行


保存文件到XML

public static void save(List<Person> persons, OutputStream outStream)
throws Exception, IllegalStateException, IOException {
XmlSerializer serializer = Xml.newSerializer();//获取XML写入信息的序列化对象
serializer.setOutput(outStream, "UTF-8");//设置要写入的OutputStream
serializer.startDocument("UTF-8", true);//设置文档标签

serializer.startTag(null, "persons");//设置开始标签,第一个参数为namespace
for (Person person : persons) {
serializer.startTag(null, "person");
serializer.attribute(null, "id", person.getId().toString());

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

serializer.startTag(null, "age");
serializer.text(person.getAge().toString());
serializer.endTag(null, "age");

serializer.endTag(null, "person");
}
serializer.endTag(null, "persons");
serializer.endDocument();
outStream.flush();
outStream.close();
}