Android DOM解析XML方法及优化

       在Android应用开发中,我们常常要在应用启动后从服务器下载一些配置文件,这些配置文件包含一些项目中可能用到的资源,这些文件很多情况下是XML文件,这时就要将XML下载到文件中保存,之后再解析XML。解析XML的方法有DOM, SAX, JDOM, DOM4J,本文中只使用了DOM,下面先介绍DOM的基础知识和解析XML的方法,然后再结合一个项目实例来实现从XML文件的下载到解析整个过程。

      DOM(Document Object Model,文档对象模型)定义了访问和操作XML的标准方法。基于DOM的XML解析器会将整个XML文档转化成对象模型集合,这个集合是树结构。这样我们就可以通过DOM接口来访问任意的节点,整个DOM树的结构和XML文档的数据分层结构相似,所以对开发者来说DOM接口的使用就很方便和直观了。但是,不足之处在于,由于这个DOM树是保存在内存中的,如果XML文档中数据很多,结构很复杂,那么就会导致其对内存的要求高,遍历一次的时间长。

      下面来看一个XML文档中数据的例子。

        

<bookstore>

<book category="children">
   <title lang="en">Harry Potter</title> 
   <author>J K. Rowling</author> 
   <year>2005</year> 
   <price>29.99</price> 
</book>

</bookstore>

该文档转化成DOM树,如下

                 

在DOM数中,XML文档中每个成分都是一个节点。

        1.整个XML文档是一个文档节点;

        2.每个XML标签是一个元素节点;

        3.包含在XML标签中的文本是一个文档节点;

        4.每个XML属性是一个属性节点;

        5.注释属于注释节点。

           需要注意的是:<year>2005</year> 中的year是一个元素节点,2005是元素节点year的子节点(文本节点)的值,不是year节点的值。

DOM中节点的属性

         nodeName节点的名称

                元素节点nodeName与标签名相同

                属性节点nodeName与属性的名称

                文本节点nodeName永远是#text

                文档节点nodeName永远是#document

         nodeValue节点的值

                元素节点的nodeValue值是undefined

                文本节点的nodeValue值是文本本身

                属性节点的nodeValue是属性值

          nodeType节点的类型

                元素节点:1

                属性节点:2

                文本节点:3

                注释节点:4

                文档节点:9

DOM中的方法

         getElementsByTagName()返回拥有指定名标签的所有元素。          

xmlDoc=loadXMLDoc("books.xml");
x=xmlDoc.getElementsByTagName("title");

       使用childNodes 或 getElementsByTagName() 属性或方法时,会返回 NodeList 。在NodeList中保存的节点不包含属性节点。

       元素节点的 attributes 属性返回属性节点的列表。

xmlDoc=loadXMLDoc("books.xml");
x=xmlDoc.getElementsByTagName("book")[0].attributes;


下面结合在项目开发中的一个实例来完整的介绍DOM解析XML方法的使用。

    1. 从服务器端下载XML文档并保存到本地

/**
 *  从服务器端下载配置文件,并保存在basesettings.xml中
 *   
 */
 private void downLoadConfigXml(){
     
	 String sValue=null;
	 sValue=PLATE_WWW+“baseconfig.xml”  //得到URL,PLATE_WWW是平台的URL
     Log.i(TAG,"baseconfig sValue=="+sValue);
     final String url=sValue;
      
     //开启一个新的线程来下载XML文档
     new Thread(){
	   public void run(){
	      HttpClient client=new DefaultHttpClient();
		  HttpGet get=new HttpGet(url);
		  HttpResponse response;
		  try{
		     response=client.execute(get);
			 HttpEntity entity=response.getEntity();
			 long length=entity.getContentLength();
			 InputSteam is=entity.getContent();
			 FileOutputStream fileOutputStream=null;
			 if(is!=null){
			    //开启存放XML文档的文件流
			    fileOutputStream=openFileOutput("baseconfig.xml",
				       Context.MODE_WORLD_READABLE+Context.MODE_WORLD_WRITLEABLE);
			    byte[]buf=new byte[1024];
				int ch=-1;
				while((ch=is.read(buf))!=-1){
				   Log.i(TAG,"ConfigXml下载中...");
				   fileOutputStream.write(buf,0,ch);
				}
			 }
			 fileOutputStream.flush();
			 //下载完毕,关闭文件流
			 if(fileOutputStream!=null){
			    fileOutputStream.close();
			 }
			 Log.i(TAG,"ConfigXml下载完成");
		  }catch(ClientProtocolException e){
		       e.printStackTrace();
			   //下载出现错误,通过Handler来做出UI上的提示
			   mHandler .obtainMessage(GlobalDef.WM_ALL_SERVER_FAIL).sendToTarget();
		  }catch(){
		       e.printStackTrace();
			   //下载出现错误,通过Handler来做出UI上的提示
			   mHandler .obtainMessage(GlobalDef.WM_ALL_SERVER_FAIL).sendToTarget();
		  }
	   }
	 }.start();	 
 }
   

     2.创建DOM解析器

/**
*  DOM解析器:包含对XML文档的加载和解析方法
*/
public class MyXml{
    Document mDocument;//代表整个XML文档树,其nodeName为#document
	Element mDocRoot;//根节点,也是一个元素节点
	NodeList mNodeList;//节点链表
	Node mNode;//节点
	int mNodeIndex=0;//遍历树时的索引
	
	//DOM加载XML文档:有以下三种
	public boolean loadXML(byte[] sXML){
	
	   DocumentBuilderFactory docFactory=DocumentBuildFactory.newInstance();
	   DocumentBuild docBuilder;
	   try{
	       docBuilder=docFactory.newDocumentBuilder();
		   InputStream is=new ByteArrayInputStream(sXML);
		   mDocument=docBuilder.parse(is,"GB2312");//文档节点
		   mDocRoot=mDocument.getDoucmentElement();//返回DOM树的根结点	   
	   }catch(Exception e){
	     e.printStackTrace();
		 return false;
	   }
	   return true;
	}
	
	
	public boolean loadXML(byte[] sXML, String sCodec)//设置编码形式
	{
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder;
		try {
			String str = new String(sXML, sCodec);
			docBuilder = docFactory.newDocumentBuilder();
			InputStream is = new ByteArrayInputStream(str.getBytes());
			
			mDocument = docBuilder.parse(is);
			mDocRoot = m_Document.getDocumentElement();
			
		} catch (Exception e) {
			e.printStackTrace();
			return false;			
		} 
		return true;
	}
	
	public boolean loadIS(InputStream is, String sCodec)
	{
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder;
		try {
			docBuilder = docFactory.newDocumentBuilder();
			mDocument = docBuilder.parse(is);
			mDocRoot = m_Document.getDocumentElement();//获取根节点
			
		} catch (Exception e) {
			e.printStackTrace();
			return false;			
		} catch (OutOfMemoryError e) {
			e.printStackTrace();
			return false;			
		}
		return true;
	}
}

所要解析的XML文档内容

<items>
<gifthead>
<gifttype>1</gifttype>
<giftname>初次相见</giftname>
<giftsort>1</giftsort>
</gifthead>
<item>
<index>100001</index>
<picname>100001.bmp</picname>
<bigpicname>100001.gif</bigpicname>
<itemname>鲜花</itemname>
</item>
<NewVer>1</NewVer>
</items>


我们通过DOM在XML中寻找节点时,往往效率低下,因为每寻找一个节点就需要从新遍历一次DOM树,在这里我在MyXml类中封装了方法GetNodeList,可以把要查询的节点的路径作为参数传入,这样就可以优化这个查询效率。比如<item>的子节点中的<picname>节点,那么以picname/item/items的形式传入,即可获得所有的item节点下的picname节点。
public boolean SelectNodeToList(String sExpress)
	{
		if(mDocRoot==null)
			return false;
		mNodeList = GetNodeList(sExpress);
		mNodeIndex = 0;
		if(mNodeList !=null)
		{
			return true;
		}
		else
			return false;
	}
	
	public NodeList GetNodeList(String sExpress)
	{
		if(mDocRoot==null)
			return null;
		try {
			String [] sNodePaths = sExpress.split("/");//用“/”来分隔String,分隔后的字段中没有“/”,得到[, , items]
			
			Element currentNode = m_DocRoot;
			List<String> sNodeTree = new ArrayList<String>();
			for(int i = 0;i<sNodePaths.length;i++)
			{
				if(sNodePaths[i].length()!=0)
				{
					sNodeTree.add(sNodePaths[i]);//“item”加到List中
				}
			}
			for(int i = 0;i<sNodeTree.size() - 1;i++)
			{
//				Log.i(i+": ");
//				Log.i(sNodeTree.get(i));
				if(!sNodeTree.get(i).equals(""))
				{
//					Log.i("currentNode = (Element) (currentNode.getElementsByTagName(sNodeTree.get(i))).item(0);");
					currentNode = (Element) (currentNode.getElementsByTagName(sNodeTree.get(i))).item(0);//获取根节点的子节点中所有名为items节点集合中的第一个节点
				}
			}
//			Log.i(sNodeTree.size()-1+": ");
//			Log.i(sNodeTree.get(sNodeTree.size()-1));
			mNodeList = currentNode.getElementsByTagName(sNodeTree.get(sNodeTree.size()-1));
			if(mNodeList.getLength() == 0 && currentNode.getNodeName().equals(sNodeTree.get(sNodeTree.size()-1)) &¤tNode == mDocRoot)
			{
				mNodeList = mDocument.getChildNodes();
			}//修正Android4.0系统收不到消息的bug
			
		} catch (Exception e) {
			Log.i(TAG, "GetNodeList m_NodeList = currentNode.getElementsByTagName(sNodeTree.get(sNodeTree.size()-1)) error.");
			e.printStackTrace();
			return null;
		}
		if(mNodeList !=null)
		{
			return mNodeList;
		}
		else
			return null;
	}
	
	public Node GetNode(String sExpress)
	{
		if(mDocRoot==null)
			return null;
		if(sExpress.equals("."))
			return m_DocRoot;
	
		try {

			String [] sNodePaths = sExpress.split("/");
			
			Element currentNode = m_DocRoot;
			List<String> sNodeTree = new ArrayList<String>();
			for(int i = 0;i<sNodePaths.length;i++)
			{
				if(sNodePaths[i].length()!=0)
				{
					sNodeTree.add(sNodePaths[i]);
				}
			}
			for(int i = 0;i<sNodeTree.size() - 1;i++)
			{
//				Log.i(i+": ");
//				Log.i(sNodeTree.get(i));
				if(!sNodeTree.get(i).equals(""))
				{
//					Log.i("currentNode = (Element) (currentNode.getElementsByTagName(sNodeTree.get(i))).item(0);");
					currentNode = (Element) (currentNode.getElementsByTagName(sNodeTree.get(i))).item(0);
				}
			}
//			Log.i(sNodeTree.size()-1+": ");
//			Log.i(sNodeTree.get(sNodeTree.size()-1));
			
			mNodeList = currentNode.getElementsByTagName(sNodeTree.get(sNodeTree.size()-1));
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		if(mNodeList !=null)
		{
			return mNodeList.item(0);
		}
		else
			return null;
	}
	
	public Node QueryNode(boolean bReset)
	{
//		Log.i("QueryNode "+bReset);
		if (bReset)
		{
			mNodeIndex = 0;
			return mNodeList.item(mNodeIndex);
		}
		
		try
		{
//			Log.i("QueryNode "+bReset);
			mNode = null;
			mNode = mNodeList.item(mNodeIndex);
			mNodeIndex++;
//			Log.i("mNodeIndex"+mNodeIndex+"="+mNode);	
		}
		catch(Exception e)
		{
			Log.w(TAG, "QueryNode mNode = mNodeList.item(mNodeIndex) error.");
		}
		
		return m_Node;
	}

	//获取m_Node节点下指定的节点的值
	public String GetValueByName(String sName)
	{
		//Log.i(TAG, "GetValueByName: sName = "+sName);
		Element element = (Element)m_Node;
		String sValue = null;
		if(sName.equals("."))
		{
			try{
				sValue = mNode.getFirstChild().getNodeValue();
			}catch(Exception e){
				Log.i(TAG, "GetValueByName("+ sName +") --> sValue = mNode.getFirstChild().getNodeValue() error.");
			}	
		}
		else
		{
			try
			{
				NodeList nodelist = element.getElementsByTagName(sName);
				if(nodelist != null)
				{
					sValue = nodelist.item(0).getFirstChild().getNodeValue();
				}
			}catch(Exception e){
//				Log.i(TAG, "GetValueByName ("+ sName +") --> sValue = nodelist.item(0).getFirstChild().getNodeValue() error.");
			}
		}
		if(sValue==null)
		{
//			Log.w(TAG, sName+"节点值sValue==null, Return");
		}
		return sValue;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值