iPhone wap浏览器之预备篇

来源:网络

点击:1746

A+ A-

所属频道:新闻中心

关键词: iPhone,wap浏览器,XML

      在笔者的上一篇文章《玩转iPhone网络通讯之BSD Socket篇》中,笔者试图在iPhone平台上利用BSD Socket搭建了一个同时兼容TCP/IP和HTTP协议进行通讯的框架,而在接下来的几篇文章里,笔者将进一步完善这个网络通讯的引擎并利用这个引擎写一个简易的wap浏览器。

      在iPhone的safari浏览器上并不支持WML的解析,尽管笔者也认为WML这种抱残守旧的技术被淘汰是迟早的事,但WML作为XML结构的一个“变种”进行学习还是不错的。

      最近浏览器技术很热,热得笔者都摸不着头脑,前段时间金山的雷军同志也投资UCWeb,尽管笔者并不觉得浏览器技术有什么高深的技术含量抑或可进行投资的价值,其实浏览器充其量是个客户端,但是既然人家大牛看好,那笔者研究研究也不无益处,或许看完本文读者也可以拿着自己的产品去找雷军同志投资一把了:)

      闲话少话,言归正传。

      上面说了,WML是XML结构的一个“变种”或者说特例,既然是特例那么就可以把它当成XML来进行解析。那么做一个浏览器的任务流程就清晰了,如下:

      封装BSD Socket进行HTTP请求。

      将请求到的WML页面解析成XML数据结构。

      渲染需要在界面上显示的WML标签(英文名tag)。

      将渲染后的WML标签显示在界面上(UIView)。

      其中第一条在笔者的前一篇文中《玩转iPhone网络通讯之BSD Socket篇》已经进行了初步的编写,当然笔者还会在下面的文章中进一步完善。

      这篇文章中着重讲解WML的解析,因为WML是XML数据的特例,解析WML也就意味这解析XML。

      说到解析XML,iPhone为程序员提供了很多工具比如NSXMLParser,这个类的接口定义如下:

      @interface NSXMLParser : NSObject {

      @private

      void * _parser;

      id _delegate;

      id _reserved1;

      id _reserved2;

      id _reserved3;

      }

      - (id)initWithContentsOfURL:(NSURL *)url; // initializes the parser with the specified URL.

      - (id)initWithData:(NSData *)data; // create the parser from data

      // delegate management. The delegate is not retained.

      - (id)delegate;

      - (void)setDelegate:(id)delegate;

      - (void)setShouldProcessNamespaces:(BOOL)shouldProcessNamespaces;

      - (void)setShouldReportNamespacePrefixes:(BOOL)shouldReportNamespacePrefixes;

      - (void)setShouldResolveExternalEntities:(BOOL)shouldResolveExternalEntities;

      - (BOOL)shouldProcessNamespaces;

      - (BOOL)shouldReportNamespacePrefixes;

      - (BOOL)shouldResolveExternalEntities;

      - (BOOL)parse; // called to start the event-driven parse. Returns YES in the event of a successful parse, and NO in case of error.

      - (void)abortParsing; // called by the delegate to stop the parse. The delegate will get an error message sent to it.

      - (NSError *)parserError; // can be called after a parse is over to determine parser state.

      @end

      从接口的定义中大致可以知道,这个类解析XML是采用SAX模式(Simple API for XML),而SAX是基于事件驱动的,其基本工作流程是分析XML文件流数据,每当发现一个新的元素时,就会产生一个对应的事件,并调用相应的用户处理函数。在iPhone上苹果公司采用了delegate模式,每发现一个新的元素时,就会调用相应的委托接口进行XML标签的处理。

      利用SAX模式解析XML占用内存少、速度快,但用户需要把解析到的XML标签自己组合成一个树状结构,从而使程序处理比较复杂。

      而对WML浏览器来说,尽管其tag并不是特别多,但是如果想完整的支持WML的tag也是一件比较枯燥的事情。所以,笔者这里采用DOM(Document Object Model)模式来解析XML文件。DOM模式在分析XML文件时,一次性的将整个XML文件流进行分析,并在内存中形成对应的树结构,同时,向用户提供一系列的接口来访问和编辑该树结构。这种方式占用内存大,速度往往慢于SAX模式,但可以给程序员提供一个面向对象的访问接口,较为方便。

      XML语言的全称是可扩展标识语言(eXtensible Markup Language),具体含义顾名思义就知道了。所谓“可扩展”,那是因为HTML等语言的不可扩展,在XML里的标签都是可以自定义的,比如WML利用XML语言自定义了一套tag,于是就有了无线wap规范。

      XML的可扩展性是指在相应的规范和标准上的扩展。首先格式要符合XML的基本要求,比如第一行要有声明,标签的嵌套层次必须前后一致等等,符合这些要求的文件,就算是一个合格的XML文件,称为Well-formatted。其次,XML文档因其内容的不同还必须在语义上符合相应的标准,这些标准由相应的“DTD文件”或者“Schema文件”来了定义,符合了这些定义要求的XML文件,称作Valid。

      笔者在本文中采用了开源的TinyXML解析器,这个解析器不会用相应的DTD文件对XML文件进行校验,但它的体积很小,只包含两个*.h文件和四个*.cpp文件。

      TinyXML是个开源的项目,更多详细的信息可以参考http://www.grinninglizard.com/tinyxml/index.html。

      下载文件包后,把相应的文件导入到项目工程中,如下图:

      

      图1

      其中tinyxml.h文件包含了全部的声明,在项目中只需要包含这个文件即可。

      Tinyxml.h中定义了很多结构,如下

      class TiXmlNode : public TiXmlBase

      {

      friend class TiXmlDocument;

      friend class TiXmlElement;

      …

      }

      这些类对应XML中的树状结构,拿下面的XML文档为例:

      《?xml version=“1.0” encoding=“utf-8” ?》

      《!-example--》

      《food》

      《name》bread《/name》

      《price unit=”$”》1.5《/price》

      《description》made in China《/description》

      《/ food 》

      其中整个XML文档用类TiXmlDocument表示,《food》、《name》、《price》、《description》等各自对应一个类TiXmlElement,XML文档的第一行对应类TiXmlDeclaration,第二行对应类TiXmlComment,文本“example”对应类TiXmlText,unit则是元素price的一个TiXmlAttribute属性。

      把TinyXML包导入到项目后,新建一个XMLParserEx.h文件和一个XMLParserEx.cpp文件来封装XML的处理,头文件定义如下:

      #ifndef _CC_XMLPARSEREX_H_

      #define _CC_XMLPARSEREX_H_

      #include 《stdio.h》

      #include “tinyxml.h”

      #define INVALID_ID -1

      class XMLParserEx

      {

      public:

      static XMLParserEx* GetInstance();

      static void Destroy();

      void RemoveAll();

      void parsexml(const char* buffer);

      void ElementParser(TiXmlNode* aParent);

      protected:

      XMLParserEx();

      ~XMLParserEx();

      private:

      static XMLParserEx* mInstance;

      };

      #endif

      XMLParserEx.cpp文件实现如下:

      #include “XMLParserEx.h”

      XMLParserEx::XMLParserEx()

      {

      }

      XMLParserEx::~XMLParserEx()

      {

      RemoveAll();

      }

      XMLParserEx* XMLParserEx::mInstance = 0;

      XMLParserEx* XMLParserEx::GetInstance()

      {

      if (mInstance == 0)

      {

      mInstance = new XMLParserEx();

      }

      return mInstance;

      }

      void XMLParserEx::Destroy()

      {

      if (mInstance)

      {

      delete mInstance;

      mInstance = 0;

      }

      }

      void XMLParserEx::RemoveAll()

      {

      }

      void XMLParserEx::ElementParser(TiXmlNode* aParent)

      {

      if(aParent == NULL)

      return;

      TiXmlNode* aChild = aParent-》FirstChild();

      while(aChild)

      {

      printf(“aChild value = %s\n”,aChild-》Value());

      int t = aChild-》Type();

      if( t == TiXmlNode::ELEMENT)

      {

      TiXmlAttribute* attr = aChild-》ToElement()-》FirstAttribute();

      if(attr)

      {

      TiXmlNode* node = aChild;

      while(node)

      {

      while(attr)

      {

      printf(“attr name = %s, attr value = %s\n”,attr-》Name(),attr-》Value());

      attr = attr-》Next();

      }

      node = node-》NextSiblingElement();

      }

      }

      ElementParser(aChild);

      }

      else if( t == TiXmlNode::TEXT)

      {

      printf(“aChild Value = %s\n”,aChild-》Value());

      }

      aChild = aChild-》NextSibling();

      }

      }

      void XMLParserEx::parsexml(const char* buffer)

      {

      TiXmlDocument* doc = new TiXmlDocument();

      printf(“xmlBuffer len = %d\n”,strlen(buffer));

      printf(“xmlBuffer is = %s\n”,buffer);

      doc-》Parse(buffer,0,TIXML_ENCODING_UTF8);

      TiXmlElement* root = doc-》RootElement();

      printf(“parse xml succeed\n”);

      ElementParser(root);

      }

    (审核编辑: 智汇小新)