android开发笔记xml数据解析方法及优缺点-kb88凯时官网登录

来自:网络
时间:2023-07-25
阅读:
目录

何为xml数据

xml 指可扩展标记语言(extensible markup language)。

可扩展标记语言(英语:extensible markup language,简称:xml)是一种标记语言,是从标准通用标记语言(sgml)中简化修改出来的。 其最主要的功能就是为了方便数据的传输与交换。

在android开发中,我们有时候也需要从服务器上获取xml数据并加以解析

如何解析xml数据

1.pull解析

这里我们根据谷歌官方的开发者文档提供的方法,也是其推荐的方法来解析xml数据。

1.分析feed中感兴趣的标签内容

例如:

  
  
  newest questions tagged android - stack overflow
  ...
      
      ...
      
      
          http://stackoverflow.com/q/9439999
          0
          where is my data file?
          
          
          
              cliff2310
              http://stackoverflow.com/users/1128925
          
          
          2012-02-25t00:30:54z
          2012-02-25t00:30:54z
          
              

i have an application that requires a data file...

... ...

就是一份xml数据,其中两个对应<>中的内容即为一个标签中的内容,比如说entry标签中嵌套的id标签为:

http://stackoverflow.com/q/9439999

其内容即为

2.选择解析器

为了解析xml数据,我们需要选择一些解析器来帮助我们分析数据。

官方文档中提到:

  xmlpullparser,这是一种在 android 上解析 xml 的高效且可维护的方式。以前,android 有此接口的两个实现:

  kxmlparser(通过 xmlpullparserfactory.newpullparser())。  

expatpullparser(通过 xml.newpullparser())。

  任一选择都可以。此部分中的示例使用 expatpullparser(通过 xml.newpullparser())。

3.实例化解析器

前文提到过,有具体的两种方式实例化解析器,分别是 用工厂类生成 或者 直接用实例生成。比如:

  //工厂类生成:
      xmlpullparserfactory factory = xmlpullparserfactory.newinstance();
      xmlpullparser xmlpullparser = factory.newpullparser();        
  //实例生成:
      xmlpullparser xmlpullparser = xml.newpullparser();

两者任选其一即可。

4.然后我们可以具体配置一下解析器

     public class stackoverflowxmlparser {
     private static final string ns = null;
     public list parse(inputstream in) throws xmlpullparserexception, ioexception {
         try {
             xmlpullparser parser = xml.newpullparser();
             parser.setfeature(xmlpullparser.
             feature_process_namespaces, false);
             parser.setinput(in, null);
             parser.nexttag();
             return readfeed(parser);
         } finally {
             in.close();
         }
      }
         ...
     }

其中,parser.setfeature(xmlpullparser.feature_process_namespaces, false);这一行是用来禁用名称空间的,当然setfeature方法还可以用来配置解析器的其他一些参数,可以查看官方的文档。

parser.setinput(in, null);是设置了具体的数据流和编码格式,如果设置为null则使用系统默认的编码。此处还可以只设置一个字节流(reader),比如:

xmlpullparser.setinput(new stringreader(xmldata));

parser.nexttag();调用 nexttag() 开始解析过程 ,官方的文档里是这样描述的:

call next() and return event if it is start_tag or end_tag otherwise throw an exception. it will skip whitespace text before actual tag if any.

就是说,如果是start_tag或end_tag,则调用next()并返回事件,否则抛出异常。它将跳过实际标记之前的空白text(如果有的话)。本质代码:

      int eventtype = next();
     if(eventtype == text &&  iswhitespace()) {   // skip whitespace
         eventtype = next();
     }
     if (eventtype != start_tag &&  eventtype != end_tag) {
         throw new xmlpullparserexception("expected start or end 
         tag", this, null);
     }
     return eventtype;

next()方法则是用来获取下一个解析事件的。

5.创建具体的方法来解析数据

  private list readfeed(xmlpullparser parser) throws 
  xmlpullparserexception, ioexception {
  list entries = new arraylist();
  parser.require(xmlpullparser.start_tag, ns, "feed");
  while (parser.next() != xmlpullparser.end_tag) {
          if (parser.geteventtype() != xmlpullparser.start_tag) {
              continue;
          }
          string name = parser.getname();
          // starts by looking for the entry tag
          if (name.equals("entry")) {
              entries.add(readentry(parser));
          } else {
              skip(parser);
          }
      }
      return entries;
  }

这里一开始的require方法是用来测试条件的,它接受三个参数,分别是预期的事件类型,名称空间,名称。

它将测试当前事件是否属于给定的类型,以及名称空间和名称是否匹配。null将匹配任何名称空间和任何名称。如果测试未通过,则抛出异常。异常文本指示解析器位置、预期事件和不满足需求的当前事件。

这里的next事件可能返回的两个值,xmlpullparser.start_tag 和 xmlpullparser.end_tag 分别对应的是开始解析一个节点和完成一个节点的解析的标志。还有一个重要的标志是xmlpullparser.end_document,对应的是解析工作完成。

skip函数则是用来跳过不感兴趣的标签的。

  private void skip(xmlpullparser parser) throws 
  xmlpullparserexception, ioexception {
  if (parser.geteventtype() != xmlpullparser.start_tag) {
      throw new illegalstateexception();
  }
  int depth = 1;
  while (depth != 0) {
      switch (parser.next()) {
      case xmlpullparser.end_tag:
          depth--;
          break;
      case xmlpullparser.start_tag:
          depth  ;
          break;
      }
  }
}

接下来则会进入到entry标签中进行进一步的解析:

  public static class entry {
  public final string title;
  public final string link;
  public final string summary;
  private entry(string title, string summary, string link) {
      this.title = title;
      this.summary = summary;
      this.link = link;
  }
 }
  // parses the contents of an entry. if it encounters a title,
   summary, or link tag, hands them off
  // to their respective "read" methods for processing. 
  otherwise, skips the tag.
  private entry readentry(xmlpullparser parser) throws 
  xmlpullparserexception, ioexception {
      parser.require(xmlpullparser.start_tag, ns, "entry");
      string title = null;
      string summary = null;
      string link = null;
      while (parser.next() != xmlpullparser.end_tag) {
          if (parser.geteventtype() != xmlpullparser.start_tag) {
              continue;
          }
          string name = parser.getname();
          if (name.equals("title")) {
              title = readtitle(parser);
          } else if (name.equals("summary")) {
              summary = readsummary(parser);
          } else if (name.equals("link")) {
              link = readlink(parser);
          } else {
              skip(parser);
          }
      }
      return new entry(title, summary, link);
  }
  // processes title tags in the feed.
  private string readtitle(xmlpullparser parser) throws 
  ioexception, xmlpullparserexception {
      parser.require(xmlpullparser.start_tag, ns, "title");
      string title = readtext(parser);
      parser.require(xmlpullparser.end_tag, ns, "title");
      return title;
  }
  // processes link tags in the feed.
  private string readlink(xmlpullparser parser) throws 
  ioexception, xmlpullparserexception {
      string link = "";
      parser.require(xmlpullparser.start_tag, ns, "link");
      string tag = parser.getname();
      string reltype = parser.getattributevalue(null, "rel");
      if (tag.equals("link")) {
          if (reltype.equals("alternate")){
              link = parser.getattributevalue(null, "href");
              parser.nexttag();
          }
      }
      parser.require(xmlpullparser.end_tag, ns, "link");
      return link;
  }
  // processes summary tags in the feed.
  private string readsummary(xmlpullparser parser) throws 
  ioexception, xmlpullparserexception {
      parser.require(xmlpullparser.start_tag, ns, "summary");
      string summary = readtext(parser);
      parser.require(xmlpullparser.end_tag, ns, "summary");
      return summary;
  }
  // for the tags title and summary, extracts their text values.
  private string readtext(xmlpullparser parser) throws ioexception, xmlpullparserexception {
      string result = "";
      if (parser.next() == xmlpullparser.text) {
          result = parser.gettext();
          parser.nexttag();
      }
      return result;
  }
  ...
  }

这里官方示例写的比较复杂,但是模块化做的较好。这里创建了一个静态内部类entry来辅助组织并返回数据。 首先开始读取后利用getname()方法来获取节点的名称,对于每个具体的节点,也分别写了不同的方法来读取。

  • 我们首先来看最基本的读取方法readtext方法,这实际上是对解析器提供的gettext方法的封装,当解析的下一个事件为text时,即利用gettext方法将其内容返回出去。
  • 接着其实其他的解析方法都大同小异,但都要根据标签内部具体的内容来设计具体的解析逻辑,比如实例中比较特殊的readlink方法,其中有一个getattributevalue方法,是根据名称空间和具体的属性名称来获取属性值的,

比如上面的代码:

string reltype = parser.getattributevalue(null, “rel”);

就是设置无名称空间,获取了link标签里rel的具体的值。

然后再根据rel的值来决定是否解析href的值。

2.简单的pull解析

上面的pull解析未免太过繁琐,在android第一行代码中也有较为简单的解析:

要解析的数据:

    
        
            1
            google maps
            1.0
        
        
            2
            chrome
            2.1
        
        
            3
            google play
            2.3
        
    

具体的解析方法:

private void parsexmlwithpull(string xmldata){
    try{
        xmlpullparserfactory factory = xmlpullparserfactory.newinstance();
        xmlpullparser xmlpullparser = factory.newpullparser();
        xmlpullparser.setinput(new stringreader(xmldata));
        int eventtype = xmlpullparser.geteventtype();
        string id = "";
        string name = "";
        string version = "";
        while(eventtype != xmlpullparser.end_document){
            string nodename = xmlpullparser.getname();
            switch (eventtype){
                case xmlpullparser.start_tag:{
                    if("id".equals(nodename)){
                        id = xmlpullparser.nexttext();
                    }else if("name".equals(nodename)){
                        name = xmlpullparser.nexttext();
                    }else if ("version".equals(nodename)){
                        version = xmlpullparser.nexttext();
                    }
                }
                break;
                case xmlpullparser.end_tag: {
                    if("app".equals(nodename)){
                        log.d("mainactivity","id is " id);
                        log.d("mainactivity","name is " name);
                        log.d("mainactivity","version is " version);
                    }
                }
                break;
                default:
                    break;
            }
            eventtype = xmlpullparser.next();
        }
    }catch (exception e){
        e.printstacktrace();
    }
}   

这里省去了一些繁琐的检测,名称空间的设置等,并且用xmlpullparser.end_document作为判断解析事件是否完成的标志。

3.sax解析

sax解析与pull解析类似,是由事件驱动的。其采用流式解析,解析与读取同步,读到哪就解析到哪。

要用sax解析,我们就需要用到接口contenthandle,一般情况下,我们可以继承系统自带的defaultdocument,并且重写其中的五个方法。

这里我们解析上面2中简单pull解析中的数据,重写defaulthandle类:

public class myhandle extends defaulthandler {
private final string tag = "mainactivity";
private string nodename;
private stringbuilder id;
private stringbuilder name;
private stringbuilder version;
@override
public void startdocument() throws saxexception{
    id = new stringbuilder();
    name = new stringbuilder();
    version = new stringbuilder();
}
@override
public void startelement(string uri, string localname, string qname, attributes attributes)
throws saxexception{
    nodename = localname;
}
@override
public void characters(char[] ch,int start,int length)throws saxexception{
    if("id".equals(nodename)){
        id.append(ch,start,length);
    }else if("name".equals(nodename)){
        name.append(ch,start,length);
    }else if("version".equals(nodename)){
        version.append(ch,start,length);
    }
}
@override
public void endelement(string uri,string localname,string qname)throws saxexception{
    if("app".equals(localname)){
        log.d(tag,"id is "  id.tostring().trim());
        log.d(tag,"name is " name.tostring().trim());
        log.d(tag,"version is " version.tostring().trim());
        id.setlength(0);
        name.setlength(0);
        version.setlength(0);
    }
}
@override
public void enddocument() throws  saxexception{
    super.enddocument();
 }
}

sax解析类似于触发器,每个方法都会在一个特定的时候被调用,startdocument方法

会在开始解析任务时执行,startelement方法会在开始解析一个节点时执行,characters是具体解析节点的过程,endelement方法会在一个节点解析完成时执行,enddocument方法会在整个解析过程完成后执行。

总的来说,sax解析的模版比较固定,语义也比较清晰。

返回顶部
顶部
网站地图