[OpenAPI] html标签分析

     个人空间的OpenAPI与facebook非常接近(从需要求上就要做成一样),所以我们在hi中也将会使用类拟于fbml这样的自定义标签(himl)。在对notifications.send接口的开发中需要传入notification参数,这个参数是通知的内容,其实就是一些html和himl标签混合在一起的字符串,其中有一个需求:用户传进一个UserName参数要求我们把它变成这个用户的真实姓名,例如:<hi:name id="billok" />。这里就是一个himl自定义标签了。我们要做的工作就是要解析类拟的自定标签把它转换成真实姓名。

     框架还是有点复杂,为了表达清楚点,这里不谈很多细节,只列一下这个subject相关的内容。

     首先,应用程序把notification参数传入notifications.send接口后,会经过一点的流程把参数html传进如下一个处理请求的基类的方法:

string ParseContent(string html){}  这个方法是用户分析html参数把其中的himl标签进行特殊处理。

        protected virtual string ParseContent(string html)
        {
            
//加载html到HtmlDocument
            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(HttpUtility.HtmlDecode(html));

            
//遍历第一层节点
            foreach (HtmlNode node in doc.DocumentNode.ChildNodes)
            {
                HtmlUtil.ForEach(node);
            }

            
return doc.DocumentNode.OuterHtml;
        }

这里我们使用了HtmlAgilityPack对html分析组件,首先把html字符串加裁成HtmlDocument对象,这样就会自动分析出文档的dom树结构,然后把我们通过遍历第一层节点,使用HtmlUtil.ForEach处理第一层根节点,处理完后就把所有的html进行输出。使用HtmlAgilityPack的好处是可以方便的对dom进行操作,可以删除节点和替换节点,不过相信还有更好的类库可以使用或者以后改为LINQ to xml进行处理。

     我们还是详细看一下ForEach方法的实现吧。

        public static void ForEach(HtmlNode node)
        {
            
if (node.NodeType != HtmlNodeType.Element)
            {
                
return;
            }
            
else
            {
                
if (!node.HasChildNodes)
                {
                    
//叶节点
                    HandleNode(node);
                }
                
else
                {
                    
foreach (HtmlNode childNode in node.ChildNodes)
                    {
                        ForEach(childNode);
                    }
                    
//子节点:有子与有父节点
                    HandleNode(node);
                }
            }
        }

这里实际上是一个递归操作,如果传进的节点不是一个Element元素(例如:不带标签的纯文件,注解等),这样的node我们是不需要处理的,所以就直接return终结循环就好了。对于Element节点的处理,就只分为有子节点和没有子节点的情况就行,如果有子节点的话就再遍历子节点并递归调用ForEach方法,目的就是把有Element处理一遍。你也发现了我们对节点的处理又被封装到另一个方法中了HandleNode:

        private static void HandleNode(HtmlNode node)
        {
            
//分析节点
            IHtmlTag iHtmlTag = ObjectFactory.CreateIHtmlTag(node.Name);
            Dictionary
<stringstring> dict = new Dictionary<stringstring>();
            //dict.Add("HtmlNode_OuterHtml", node.OuterHtml);//可以传入节点的全部html,以作进一步处理
            foreach (HtmlAttribute attr in node.Attributes)
            {
                
if (!dict.ContainsKey(attr.Name))
                {
                    dict.Add(attr.Name, attr.Value);
                }
            }
            HtmlTag htmlTag 
= iHtmlTag.Handle(dict);

            
//处理节点
            switch (htmlTag.HandleType)
            {
                
case HtmlTagHandleType.Remove:
                    
if (node.ParentNode != null)
                        node.ParentNode.RemoveChild(node);
                    
break;
                
case HtmlTagHandleType.Replace:
                    
if (node.ParentNode != null)
                    {
                        HtmlNode newNode 
= HtmlNode.CreateNode(htmlTag.Html);
                        node.ParentNode.ReplaceChild(newNode, node);
                    }
                    
break;
                
case HtmlTagHandleType.Unknown:
                
default:
                    
//不做任务处理
                    break;

            }
        }

这个方法分为两个步聚:分析节点和处理节点,并且利用了IOC模式。

IOC模式是系统框架的一部分,这里就不详细谈了,你只要知道从ObjectFactory.CreateIHtmlTag方法可以得到标签名(如:hi:name)所以指定的关于IHtmlTag接口的一个实现,如果没有合符处理的实现就会使用一个默认实现(就是告诉"处理节点"步聚什么都不用做)。对于IHtmlTag接口我们看一下定义:

    public enum HtmlTagHandleType
    {
        Unknown,
        Remove,
        Replace,
    }

    
public class HtmlTag
    {
        
public HtmlTagHandleType HandleType { getset; }
        
public string Html { getset; }

        
public HtmlTag()
        {
            HandleType 
= HtmlTagHandleType.Unknown;
            Html 
= string.Empty;
        }
    }

    
public interface IHtmlTag
    {
        HtmlTag Handle(IDictionary
<stringstring> attributes);
    }

IHtmlTag接口只有一个方法

HtmlTag Handle(IDictionary<string, string> attributes); 接受一个节点属性的键值对字典,返回对这些键值对进行处理的结果。

返回的HtmlTag可以指示出需要后续进行怎么样的操作(HtmlTagHandleType),以及处理完后的html

Okay! 对节点进行分析后得到HtmlTag,现在就可以进行节点处理的工作了。节点的处理方式以HtmlTagHandleType的返回为准,分别是Remove删除节点,Replace替换节点,不作处理。


经过这些处理后,返加doc.DocumentNode.OuterHtml就可以得到处理后的html内容了。通过实现IHtmlTag接口,可以很方便的添加新的himl标签对内容时行特殊处理了。并用标签还可以任竟扩展属性,例如:<hi:name id="billok,junbiaochen" target="abc" /> 等等。

 

 

 

posted @ 2008-11-25 11:26  网际飞狐  阅读(627)  评论(0编辑  收藏  举报