欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

详解Android中解析XML的方法

程序员文章站 2024-03-02 12:39:46
xml在各种开发中都广泛应用,android也不例外。作为承载数据的一个重要角色,如何读写xml成为android开发中一项重要的技能。今天就由我向大家介绍一下在andro...

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解析器。

以上三种解析器,都是非常实用的解析器,我将会一一介绍。我们将会使用这三种解析技术完成一项共同的任务。

我们新建一个项目,项目结构如下:
详解Android中解析XML的方法

我会在项目的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的代码:

package com.scott.xml.model; 
 
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文档,如图:详解Android中解析XML的方法

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

<?xml version="1.0" encoding="utf-8"?> 
<books> 
  <book id="1001"> 
    <name>thinking in java</name> 
    <price>80.0</price> 
  </book> 
  <book id="1002"> 
    <name>core java</name> 
    <price>90.0</price> 
  </book> 
  <book id="1003"> 
    <name>hello, andriod</name> 
    <price>100.0</price> 
  </book> 
</books> 

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

package com.scott.xml.parser; 
 
import java.io.inputstream; 
import java.util.list; 
 
import com.scott.xml.model.book; 
 
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代码如下:

package com.scott.xml.parser; 
 
import java.io.inputstream; 
import java.io.stringwriter; 
import java.util.arraylist; 
import java.util.list; 
 
import javax.xml.parsers.saxparser; 
import javax.xml.parsers.saxparserfactory; 
import javax.xml.transform.outputkeys; 
import javax.xml.transform.result; 
import javax.xml.transform.transformer; 
import javax.xml.transform.transformerfactory; 
import javax.xml.transform.sax.saxtransformerfactory; 
import javax.xml.transform.sax.transformerhandler; 
import javax.xml.transform.stream.streamresult; 
 
import org.xml.sax.attributes; 
import org.xml.sax.saxexception; 
import org.xml.sax.helpers.attributesimpl; 
import org.xml.sax.helpers.defaulthandler; 
 
import com.scott.xml.model.book; 
 
public class saxbookparser implements bookparser { 
 
  @override 
  public list<book> parse(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); 
      } 
    } 
  } 
} 

代码中,我们定义了自己的事件处理逻辑,重写了defaulthandler的几个重要的事件方法。下面我为大家着重介绍一下defaulthandler的相关知识。defaulthandler是一个事件处理器,可以接收解析器报告的所有事件,处理所发现的数据。它实现了entityresolver接口、dtdhandler接口、errorhandler接口和contenthandler接口。这几个接口代表不同类型的事件处理器。我们着重介绍一下contenthandler接口。结构如图:
详解Android中解析XML的方法
这几个比较重要的方法已被我用红线标注,defaulthandler实现了这些方法,但在方法体内没有做任何事情,因此我们在使用时必须覆写相关的方法。最重要的是startelement方法、characters方法和endelement方法。当执行文档时遇到起始节点,startelement方法将会被调用,我们可以获取起始节点相关信息;然后characters方法被调用,我们可以获取节点内的文本信息;最后endelement方法被调用,我们可以做收尾的相关操作。

最后,我们需要调用sax解析程序,这个步骤在mainactivity中完成:

package com.scott.xml; 
import java.io.fileoutputstream; 
import java.io.inputstream; 
import java.util.list; 
 
import android.app.activity; 
import android.content.context; 
import android.os.bundle; 
import android.util.log; 
import android.view.view; 
import android.widget.button; 
 
import com.scott.xml.model.book; 
import com.scott.xml.parser.bookparser; 
import com.scott.xml.parser.saxbookparser; 
 
public class mainactivity extends activity { 
 
  private static final string tag = "xml"; 
 
  private bookparser parser; 
  private list<book> books; 
 
  @override 
  public void oncreate(bundle savedinstancestate) { 
    super.oncreate(savedinstancestate); 
    setcontentview(r.layout.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(); //创建saxbookparser实例 
          books = parser.parse(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()); 
        } 
      } 
    }); 
  } 
} 

界面就两个按钮,顺便给大家贴上:
详解Android中解析XML的方法
点击“readxml”按钮,将会调用sax解析器解析文档,并在日志台打印相关信息:详解Android中解析XML的方法
然后再点击“writexml”按钮,将会在该应用包下的files目录生成一个books.xml文件:详解Android中解析XML的方法

使用dom解析器:
dombookparser.java代码如下:

package com.scott.xml.parser; 
 
import java.io.inputstream; 
import java.io.stringwriter; 
import java.util.arraylist; 
import java.util.list; 
 
import javax.xml.parsers.documentbuilder; 
import javax.xml.parsers.documentbuilderfactory; 
import javax.xml.transform.outputkeys; 
import javax.xml.transform.result; 
import javax.xml.transform.source; 
import javax.xml.transform.transformer; 
import javax.xml.transform.transformerfactory; 
import javax.xml.transform.dom.domsource; 
import javax.xml.transform.stream.streamresult; 
 
import org.w3c.dom.document; 
import org.w3c.dom.element; 
import org.w3c.dom.node; 
import org.w3c.dom.nodelist; 
 
import com.scott.xml.model.book; 
 
public class dombookparser implements bookparser { 
 
  @override 
  public list<book> parse(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(); 
  } 
 
} 

然后再mainactivity中只需改一个地方:

readbtn.setonclicklistener(new view.onclicklistener() { 
  @override 
  public void onclick(view v) { 
    try { 
      inputstream is = getassets().open("books.xml"); 
//     parser = new saxbookparser(); 
      parser = new dombookparser(); 
      books = parser.parse(is); 
      for (book book : books) { 
        log.i(tag, book.tostring()); 
      } 
    } catch (exception e) { 
      log.e(tag, e.getmessage()); 
    } 
  } 
}); 

执行结果是一样的。

使用pull解析器:
pullbookparser.java代码如下:

package com.scott.xml.parser; 
 
import java.io.inputstream; 
import java.io.stringwriter; 
import java.util.arraylist; 
import java.util.list; 
 
import org.xmlpull.v1.xmlpullparser; 
import org.xmlpull.v1.xmlserializer; 
 
import android.util.xml; 
 
import com.scott.xml.model.book; 
 
public class pullbookparser implements bookparser { 
 
  @override 
  public list<book> parse(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做以下更改:

readbtn.setonclicklistener(new view.onclicklistener() { 
  @override 
  public void onclick(view v) { 
    try { 
      inputstream is = getassets().open("books.xml"); 
//     parser = new saxbookparser(); 
//     parser = new dombookparser(); 
      parser = new pullbookparser(); 
      books = parser.parse(is); 
      for (book book : books) { 
        log.i(tag, book.tostring()); 
      } 
    } catch (exception e) { 
      log.e(tag, e.getmessage()); 
    } 
  } 
}); 

和其他两个执行结果都一样。

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。