Android XML解析学习——Dom方式 .

时间:2022-08-02 20:33:01

一.基础知识

在《Android XML解析学习——Sax方式》和《Android XML解析学习——Sax方式(续)》中我们学习了在Android 平台上使用SAX的方式解析XML文档的方法。除了SAX的方式,还有一种常用的为DOM(Document Object Model文档对象模型)方式。

DOM方式解析XML的过程为首先将所有 XML 文档读取到内存中,构造一个树结构,然后允许使用 DOM API 来操作整个树结构,包括遍历 XML 树、检索所需的数据等。

Android平台使用Java SDK中的API来实现DOM方式的解析,具体的方法和之前学习的使用Java SDK中的SAX方式解析XML类似,因此有了前面SAX的基础,学习DOM方式就会比较简单了。DOM 和 之前的SAX 的主要差别是 API 本身的结构,SAX 由基于事件的回调集组成,边解析边产生事件从而调用事件处理器的回调函数进行处理。而DOM不是,DOM直接读取整个XML树保存为Document类,因此只需处理保存在内存中的Document对象即可,没有回调函数。

DOM解析相关的包为javax.xml.parsers和org.w3c.dom。其中javax.xml.parsers提供Document构造器工厂DocumentBuilderFactory和Document构造器DocumentBuilder,DocumentBuilderFactory实例调用newDocumentBuilder()创建构造器DocumentBuilder实例,然后该实例就可以调用parse方法来返回一个解析为Document类的实例。Document 类由表示XML文档的包含元素、属性、文本内容等的各个节点组成。因此有了Document对象后就可以按需要获取xml文档中各个子节点及其相关的值了。Document、Element、Node等各个和DOM相关的具体部分都在org.w3c.dom包中,因此和SAX类似,org.w3c.dom包是底层具体的负责DOM解析相关的内容,并且为上层javax.xml.parsers包提供DOM解析器等相关调用。

 

下面我们就用上面介绍的DOM方式来实现解析XML形式的USGS地震数据的Demo例子。

二.实例开发

我们要完成的效果图如下图1所示:

Android XML解析学习——Dom方式 .

 

图1 ListView列表显示的地震数据

和上一部分Demo例子的一样,也是解析完地震数据后用ListView列表的方式显示每条地震的震级和地名信息。

新建一个Android工程AndroidXMLDemoDom。

要添加的基本内容和上一个Demo中的一样,这里就不再赘述,这次要添加的解析器新类为DomEarthquakeHandler,内容如下所示:

 

[java] view plaincopyprint?
  1. public class DomEarthquakeHandler {  
  2.     //xml解析用到的Tag   
  3.     private String kEntryElementName = "entry";  
  4.     private String kLinkElementName = "link";  
  5.     private String kLinkAttributeName = "href";  
  6.     private String kTitleElementName = "title";  
  7.     private String kUpdatedElementName = "updated";  
  8.     private String kGeoRSSPointElementName = "georss:point";  
  9.     private String kGeoRSSElevElementName = "georss:elev";  
  10.     //用于保存xml解析获取的结果   
  11.     private ArrayList<EarthquakeEntry> earthquakeEntryList;  
  12.     private EarthquakeEntry earthquakeEntry = null;  
  13.     //解析xml数据   
  14.     public ArrayList<EarthquakeEntry> parse(InputStream inStream)  
  15.     {  
  16.         earthquakeEntryList = new ArrayList<EarthquakeEntry>();  
  17.         //创建DocumentBuilder   
  18.         DocumentBuilderFactory docBFactory = DocumentBuilderFactory.newInstance();  
  19.         DocumentBuilder docBuilder = null;  
  20.         try {  
  21.             docBuilder = docBFactory.newDocumentBuilder();  
  22.         } catch (ParserConfigurationException e1) {  
  23.             // TODO Auto-generated catch block   
  24.             e1.printStackTrace();  
  25.         }     
  26.         //解析地震数据流.   
  27.         Document doc = null;  
  28.         try {  
  29.             doc = docBuilder.parse(inStream);  
  30.         } catch (SAXException e1) {  
  31.             // TODO Auto-generated catch block   
  32.             e1.printStackTrace();  
  33.         } catch (IOException e1) {  
  34.             // TODO Auto-generated catch block   
  35.             e1.printStackTrace();  
  36.         }  
  37.         Element docEle = doc.getDocumentElement();  
  38.           
  39.         // Get a list of each earthquake entry.   
  40.         NodeList nList = docEle.getElementsByTagName(kEntryElementName);  
  41.         if(nList != null && nList.getLength() > 0)  
  42.         {  
  43.             for(int i=0; i < nList.getLength(); i++)  
  44.             {  
  45.                 try {  
  46.                     Element entry = (Element)nList.item(i);  
  47.                     Element title = (Element)entry.getElementsByTagName(kTitleElementName).item(0);  
  48.                     Element when = (Element)entry.getElementsByTagName(kUpdatedElementName).item(0);  
  49.                     Element gpoint = (Element)entry.getElementsByTagName(kGeoRSSPointElementName).item(0);  
  50.                     Element gelev = (Element)entry.getElementsByTagName(kGeoRSSElevElementName).item(0);  
  51.                     Element link = (Element)entry.getElementsByTagName(kLinkElementName).item(0);  
  52.                       
  53.                     String details = title.getFirstChild().getNodeValue();  
  54.                     String point = gpoint.getFirstChild().getNodeValue();  
  55.                     String elevS = gelev.getFirstChild().getNodeValue();  
  56.                     String date = when.getFirstChild().getNodeValue();  
  57.                     //获取link链接   
  58.                     String webLink = link.getAttribute(kLinkAttributeName);  
  59.     //              Log.v("Dom", webLink);   
  60.                     //构造更新时间   
  61.                     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");  
  62.                     Date qdate = new GregorianCalendar(0,0,0).getTime();  
  63.                     try {  
  64.                       qdate = sdf.parse(date);  
  65.                     } catch (ParseException e) {  
  66.                       e.printStackTrace();  
  67.                     }  
  68.                     //提取经纬度信息   
  69.                     String[] latLongitude = point.split(" ");  
  70.                     Location location = new Location("dummyGPS");  
  71.                     location.setLatitude(Double.parseDouble(latLongitude[0]));  
  72.                     location.setLongitude(Double.parseDouble(latLongitude[1]));  
  73.                     //提取强度信息   
  74.                     String magnitudeString = details.split(" ")[1];  
  75.                     int end =  magnitudeString.length()-1;  
  76.                     double magnitude = Double.parseDouble(magnitudeString.substring(0, end));  
  77.                     //提取位置信息   
  78.                     String place = details.split(",")[1].trim();  
  79.                     //提取海拔高度信息   
  80.                     double evel;  
  81.                     //因为USGS数据有可能会输错,比如为"--10000",多了一个"-"号   
  82.                     try {  
  83.                         evel = Double.parseDouble(elevS);  
  84.                     } catch (Exception e) {  
  85.                         // TODO: handle exception   
  86.                         e.printStackTrace();  
  87.                         evel = 0;  
  88.                     }  
  89.                     Log.v("Dom_Elev", String.valueOf(evel));                      
  90.                     earthquakeEntry = new EarthquakeEntry(qdate, place, webLink, location, magnitude, evel);  
  91.                       
  92.                     earthquakeEntryList.add(earthquakeEntry);     
  93.                 } catch (Exception e) {  
  94.                     // TODO: handle exception   
  95.                     e.printStackTrace();  
  96.                 }  
  97.             }  
  98.         }  
  99.         Log.v("Dom", "End");  
  100.         return earthquakeEntryList;  
  101.     }  
  102. }  

 

程序首先也是定义解析时需要用到的元素Tag名称变量,其中因为DOM没有像SAX方式那样可以把一个元素的的名称分开为表示名称空间的Uri和当前标签名的localname,只是通过getElementsByTagName方法由完整的名称读取,因此需要定义完整的元素标签名称:

 

[java] view plaincopyprint?
  1. private String kGeoRSSPointElementName = "georss:point";  
  2. private String kGeoRSSElevElementName = "georss:elev";  

 

在定义的用于解析xml数据的方法中

public ArrayList<EarthquakeEntry> parse(InputStream inStream)

使用工厂类创建Document构造器DocumentBuilder:

[java] view plaincopyprint?
  1. //创建DocumentBuilder   
  2.     DocumentBuilderFactory docBFactory = DocumentBuilderFactory.newInstance();  
  3.     DocumentBuilder docBuilder = null;  
  4.     try {  
  5.         docBuilder = docBFactory.newDocumentBuilder();  
  6.     } catch (ParserConfigurationException e1) {  
  7.         // TODO Auto-generated catch block   
  8.         e1.printStackTrace();  
  9.     }     

然后调用DocumentBuilder实例的parse方法解析xml文档数据并返回Document对象,并通过getDocumentElement()方法获取文档的根元素。

 

[java] view plaincopyprint?
  1. //解析地震数据流.   
  2. Document doc = null;  
  3. try {  
  4.     doc = docBuilder.parse(inStream);  
  5. } catch (SAXException e1) {  
  6.     // TODO Auto-generated catch block   
  7.     e1.printStackTrace();  
  8. } catch (IOException e1) {  
  9.     // TODO Auto-generated catch block   
  10.     e1.printStackTrace();  
  11. }  
  12. Element docEle = doc.getDocumentElement();  

有了XML文档的根元素,就可以通过getElementsByTagName等方法获取具体的子元素,及子节点和节点值等。具体方法可以参考以上的代码。

最后添加AndroidXMLDemoDom.java文件中的内容,内容和前一个Demo工程AndroidXMLDemoSaxII中的AndroidXMLDemoSaxII.java基本一样,

[java] view plaincopyprint?
  1. public class AndroidXMLDemoDom extends Activity {  
  2.     /** Called when the activity is first created. */  
  3.     //定义显示的List相关变量   
  4.     ListView list;  
  5.     ArrayAdapter<EarthquakeEntry> adapter;  
  6.     ArrayList<EarthquakeEntry> earthquakeEntryList;  
  7.     @Override  
  8.     public void onCreate(Bundle savedInstanceState) {  
  9.         super.onCreate(savedInstanceState);  
  10.         setContentView(R.layout.main);  
  11.           
  12.         //获取地震数据流   
  13.         InputStream earthquakeStream = readEarthquakeDataFromFile();  
  14.       //Dom方式进行xml解析   
  15.       DomEarthquakeHandler domHandler = new DomEarthquakeHandler();  
  16.       earthquakeEntryList = domHandler.parse(earthquakeStream);  
  17.         //用ListView进行显示   
  18.         list = (ListView)this.findViewById(R.id.list);  
  19.         adapter = new ArrayAdapter<EarthquakeEntry>(this, android.R.layout.simple_list_item_1, earthquakeEntryList);  
  20.         list.setAdapter(adapter);  
  21.     }  
  22.       
  23.     private InputStream readEarthquakeDataFromFile()  
  24.     {  
  25.         //从本地获取地震数据   
  26.         InputStream inStream = null;  
  27.         try {  
  28.             inStream = this.getAssets().open("USGS_Earthquake_1M2_5.xml");  
  29.         } catch (IOException e) {  
  30.             // TODO Auto-generated catch block   
  31.             e.printStackTrace();  
  32.         }  
  33.         return inStream;  
  34.     }  
  35.     private InputStream readEarthquakeDataFromInternet()  
  36.     {  
  37.         //从网络上获取实时地震数据   
  38.         URL infoUrl = null;  
  39.         InputStream inStream = null;  
  40.         try {  
  41.             infoUrl = new URL("http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml");  
  42.             URLConnection connection = infoUrl.openConnection();  
  43.             HttpURLConnection httpConnection = (HttpURLConnection)connection;  
  44.             int responseCode = httpConnection.getResponseCode();  
  45.             if(responseCode == HttpURLConnection.HTTP_OK)  
  46.             {  
  47.                 inStream = httpConnection.getInputStream();  
  48.             }  
  49.         } catch (MalformedURLException e) {  
  50.             // TODO Auto-generated catch block   
  51.             e.printStackTrace();  
  52.         } catch (IOException e) {  
  53.             // TODO Auto-generated catch block   
  54.             e.printStackTrace();  
  55.         }  
  56.         return inStream;  
  57.     }  
  58. }  

 

只是把进行XML解析的部分换成了如下方式:

[java] view plaincopyprint?
  1. //Dom方式进行xml解析   
  2.    DomEarthquakeHandler domHandler = new DomEarthquakeHandler();  
  3.    earthquakeEntryList = domHandler.parse(earthquakeStream);  

完成了,可以保存运行看下效果。

 

这样我们就已经学习了Android上使用SAXDOM两种方式解析XML的方法,下面我们简单总结比较一下两者的区别。

DOM解析器是通过将XML文档解析成树状模型并将其放入内存来完成解析工作的,而后对文档的操作都是在这个树状模型上完成的。这个在内存中的文档树将是 文档实际大小的几倍。这样做的好处是结构清除、操作方便,而带来的麻烦就是极其耗费系统资源。而SAX正好克服了DOM的缺点。SAX解析器的处理过程是 通读整个文档,根据文档内容产生事件,而把对这些事件的处理交由事件处理器处理。SAX不需要在内存中保存整个文档,它对系统资源的节省是显而易见的。这 样在一些需要处理大型XML文档和性能要求比较高的场合就要用SAX了。

我们以表格的形式看一下他们在一些方面的比较:

SAX

DOM

顺序读入文档并产生相应事件,可以处理任何大小的XML文档

在内存中创建文档树,不适于处理大型XML文档

只能对文档按顺序解析一遍,不支持对文档的随意访问

可以随意访问文档树的任何部分,没有次数限制

只能读取XML文档内容,而不能修改

可以随意修改文档树,从而修改XML文档

开发上比较复杂,需要自己来实现事件处理器

易于理解,易于开发

通过比较我们可以知道SAX和DOM各自适合的场合:

SAX适于处理下面的问题:

1. 对大型文档进行处理

2. 只需要文档的部分内容,或者只需要从文档中得到特定信息

DOM适合处理下面的问题:

1. 需要对文档进行修改

2. 需要随机对文档进行访问

以上只是对SAX和DOM方式传统的比较,而对具体的在Android平台上使用来说,因为DOM更加占用内存,而对运行在移动设备上的Android系统来说往往内存是一个比较稀缺的资源,因此除非XML 文档始终保持很小的大小,否则相对来说推荐在Android 应用程序中使用SAX 方式解析XML 。并且Android系统为SAX还提供了额外的实用工具android.util.Xml和特有的包android.sax等,因此实际上在Android使用SAX也一样很方便了。

三.总结

在这部分中我们学习了使用DOM方式解析XML的方法,并且比较了DOM方式和SAX方式的各种特点及区别。

从以上的学习我们知道Android平台已经为XML的解析提供了强大的功能,既可以使用Java SDK中的SAX和DOM方式来解析,还可以使用Android SDK提供的SAX方式。但Android平台上还有一种解析XML的方式,就是使用Pull解析器,这部分内容我们以后接着学习。