我对现在的解析html的算法还是比较满意的。现阶段,经过我的一些测试,添加了对BR,IUPUT,IMG,META,SCRIPT,注释等特殊html语法的支持。对于完全符合html语法规范的页面,效果是令人满意的。不足之处是缺乏对错误的包容能力,对不符合html语法的页面可能解析出错或者异常。如果要把它做成完善的html解析器,我认为必须实现一个优先级算法,提供错误的包容能力。不过我想,这个问题相当复杂,暂时不能很好的解决。现在,我决定采取另外一种折中的方式,即只解析自定义的控件,而不处理普通的html控件。以现在的算法,可以很轻易的实现这个功能。
个人觉得,使用用户控件,然后在控件输出的时候截获render应该是可取的。但个人感觉这种方式实际使用负载,不够灵活。我个人更倾向于实现一个轻量级的、易控的静态页面生成类库,可以轻松自由的进行自定义和扩展。对于大多数网站来说,首页和分类页面的请求都是很频繁的,并且内容也需要经常性的更新,使用Asp.Net的缓存机制是一个很好的选择;但是对于网站的内容页,如新闻或者软件下载的内容页面,相对来说请求频率要低得多,动态生成或者使用缓存都是不合适的。这种情况最好的选择是生成静态页面,然后保存在硬盘上。另外,对于实时性要求不高的首页或者分类页,也可以结合shtml的include机制,将内容部分用模板的方式静态生成保存在硬盘上。
正如本文开头所说,由于联系暑期实习单位的事情,最近几天都暂停开发了。在这里我还是先将上一篇文章没说完的继续说完。
整个解析模块主要由三个类来构成:StaticControlFactory、ParseStatusManager和StaticBaseControl构成的。其中StaticControlFactory主要负责对模板进行字符分析处理和控件的处理;ParseStatusManager主要负责对控件解析状态进行处理,以及对StaticControlFactory发出处理命令;StaticBaseControl是实体类,保存相关结果。
代码结构自认为还是很清晰的,并且有很多注释,我就不详细解释了,主要说明几个关键的地方。StaticControlFactory中使用了堆栈来保存控件的层次结构。每当创建了新的控件,就将新控件加入到当前控件的子控件集合中,然后将当前控件推入堆栈,新控件成为当前控件。当新控件解析完毕后则反向操作。ParseStatusManager的状态管理也采用了相似的方式,只是没有前者的层次结构。每次StaticControlFactory读取到边界字符时,就调用ParseStatusManager对象的ChangeStatus方法,实现相应的状态转化。ParseStatusManager对StaticControlFactory的操作,我使用了代理结合Command模式的方式来实现。这种方式最大的好处是ParseStatusManager不需要维护StaticControlFactory的一大堆方法签名,只需要传入合适的命令就可以了。如果需要在处理状态转换的时候,需要增加新的处理,只需要使StaticControlFactory增加对一个新的命令的支持就可以了。
为了增加对不符合<flag name=value>body</flag>形式的标签,包括img、br之类可以无结尾或使用/>简短结束标志的标签的支持,对StaticControlFactory增加了几个方法,ReadNextWord用以获取控件开始标签后的下一个单词,以判断当前标签是否是定义的特殊标签,ProcessForScriptBlock用以专门处理Script模块。如果希望将类库改成指处理我们定义的特殊标签,就可以在每次读到控件开始标记的时候使用ReadNextWord读取控件类型,在判断正确后才开始控件的解析操作;对每次读到控件结束标记时也进行相似操作。
现在的方案实际上是我的第二个方案。第一个方案使用了递归,思路不是很清晰,调试起来相当麻烦,现在的方案要清晰多了。对于数据绑定,在我的第一个方案里已经部分实现了,主要采用了遍历加反射的方式。遗憾的是还只支持了对对象数组这种数据源的支持,主要原因是我现在的项目使用了petshop3的结构,以数据实体的方式传递数据,并且没有实现repeate之类控件嵌套形式下的绑定。不过预计这两个问题都比较简单,可以比较快的实现。我会尽快实现一个可使用的版本发布出来。