自己写的简单的模板引擎

Posted 秋荷雨翔的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自己写的简单的模板引擎相关的知识,希望对你有一定的参考价值。

    开发网站需要用到模板引擎,自己写了一个简单的。功能很简单,只做了两件事,一个是替换Model一个是替换List,支持List嵌套。

html模板示例代码如下:

技术分享
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <script type="text/javascript" src="/Scripts/jquery/jquery-1.7.1.min.js"></script>
    <script type="text/javascript" src="/Site/common.js"></script>
    <script type="text/javascript">
        $(function () {
            $("title").html("${siteInfo.siteName}");
        });
    </script>
</head>
<body style="background-color: #fff; font-size: 14px;">
    <span style="color: red;">网站基本信息</span><br />
    <img alt="" src="${siteInfo.logoUrl}" style="width: 100px; height: 100px;" />
    <span>${siteInfo.siteName}</span>
    <br />
    <span style="color: red;">菜单</span>
    <!-- 菜单 开始 ===================================================================================== -->
    <ul id="rootMenu">
        <#foreach list="channel" model="rootMenu" where="siteId=1 and level=1"  >
        <li>
            <img alt="" src="${rootMenu.iconUrl}" style="height: 30px; width: 30px;" />
            <a href="/Home/List?channelId=${rootMenu.id}&page=1">${rootMenu.title}</a>
        </li>
        <ul>
            <#foreach list="channel" model="subMenu" where="parentId=${rootMenu.id}"  >
            <li>
                <img alt="" src="${subMenu.iconUrl}" style="height: 30px; width: 30px;" />
                <a href="/Home/List?channelId=${subMenu.id}&page=1">${subMenu.title},${subMenu.sort}</a>
            </li>
            <#/foreach>
        </ul>
        <#/foreach>
    </ul>
    <!--菜单 结束 ====================================================================================== -->
    <span style="color: red;">新闻活动</span>
    <!-- 内容列表 开始 ===================================================================================== -->
    <ul>
        <#foreach list="content" model="con" where="channel.id=1" page="1" pageSize="5">
        <li>
            <img alt="" src="${con.imgUrl}" style="height: 20px; width: 20px;" />
            <a href="/Home/Content?contentId=${con.id}">${con.title}</a>
            ${con.publishTime}
        </li>
        <#/foreach>
    </ul>
    <!-- 内容列表 结束 ===================================================================================== -->
    <span style="color: red;">社会责任</span>
    <!-- 内容列表 开始 ===================================================================================== -->
    <ul>
        <#foreach list="content" model="con" where="channel.id=1" page="1" pageSize="5" >
        <li>
            <img alt="" src="${con.imgUrl}" style="height: 20px; width: 20px;" />
            <a href="/Home/Content?contentId=${con.id}">${con.title}</a>
            ${con.publishTime}
        </li>
        <#/foreach>
    </ul>
    <!-- 内容列表 结束 ===================================================================================== -->
</body>
</html>
View Code

模板引擎代码如下,设计的可能不是太好,不过很简单明了:

技术分享
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using Models;

namespace DAL
{
    /// <summary>
    /// 模板引擎
    /// </summary>
    public class TemplateDal
    {
        #region 载入模板
        /// <summary>
        /// 载入模板
        /// </summary>
        public static string LoadTemplate(HttpServerUtilityBase server, string url)
        {
            string result = string.Empty;
            StreamReader sr = new StreamReader(server.MapPath(url), Encoding.UTF8);
            result = sr.ReadToEnd();
            sr.Close();
            return result;
        }
        #endregion

        #region 替换页面model
        /// <summary>
        /// 替换页面model
        /// </summary>
        public static string ReplaceModels(string pageHtml, Dictionary<string, object> data)
        {
            Regex reg = new Regex(@"\${([^\${}]+)\.([^\${}]+)}", RegexOptions.IgnoreCase);
            MatchCollection mc = reg.Matches(pageHtml);
            foreach (Match m in mc)
            {
                if (data.Keys.Contains<string>(m.Groups[1].Value))
                {
                    object model = data[m.Groups[1].Value];
                    Type type = model.GetType();
                    PropertyInfo propertyInfo = type.GetProperty(m.Groups[2].Value);
                    object obj = propertyInfo.GetValue(model, null);
                    string val = string.Empty;
                    if (obj != null)
                    {
                        if (obj.GetType() == typeof(DateTime))
                        {
                            val = ((DateTime)obj).ToString("yyyy-MM-dd");
                        }
                        else
                        {
                            val = obj.ToString();
                        }
                    }
                    pageHtml = pageHtml.Replace("${" + m.Groups[1].Value + "." + m.Groups[2].Value + "}", val);
                }
            }
            return pageHtml;
        }
        #endregion

        #region 替换列表的model
        /// <summary>
        /// 替换列表的model
        /// </summary>
        public static string ReplaceModels(string html, string model, object data)
        {
            Regex reg = new Regex(@"\${[^\${}]+\.([^\${}]+)}", RegexOptions.IgnoreCase);
            MatchCollection mc = reg.Matches(html);
            foreach (Match m in mc)
            {
                Type type = data.GetType();
                PropertyInfo propertyInfo = type.GetProperty(m.Groups[1].Value);
                object obj = propertyInfo.GetValue(data, null);
                string val = string.Empty;
                if (obj != null)
                {
                    if (obj.GetType() == typeof(DateTime))
                    {
                        val = ((DateTime)obj).ToString("yyyy-MM-dd");
                    }
                    else
                    {
                        val = obj.ToString();
                    }
                }
                html = html.Replace("${" + model + "." + m.Groups[1].Value + "}", val);
            }
            return html;
        }
        #endregion

        #region 判断页面是否存在 foreach 标签
        /// <summary>
        /// 判断页面是否存在 foreach 标签
        /// </summary>
        public static bool HasForeach(string html)
        {
            Regex reg = new Regex(@"<#foreach[\s]+[^<>]*>", RegexOptions.IgnoreCase | RegexOptions.Singleline);
            return reg.IsMatch(html);
        }
        #endregion

        #region 替换foreach
        /// <summary>
        /// 替换foreach
        /// </summary>
        public static string ReplaceLists(string pageHtml)
        {
            #region 生成标签集合
            Regex reg = new Regex(@"<#foreach[\s]+[^<>]*>", RegexOptions.IgnoreCase | RegexOptions.Singleline);
            Regex reg2 = new Regex(@"<#/foreach>", RegexOptions.IgnoreCase | RegexOptions.Singleline);
            MatchCollection mc = reg.Matches(pageHtml);
            MatchCollection mc2 = reg2.Matches(pageHtml);
            List<Tag> tagList = new List<Tag>();
            foreach (Match m in mc)
            {
                Tag tag = new Tag(m.Index, TagType.TagStart, m.Value);
                tagList.Add(tag);
            }
            foreach (Match m in mc2)
            {
                Tag tag = new Tag(m.Index, TagType.TagEnd, m.Value);
                tagList.Add(tag);
            }
            tagList.Sort((a, b) => a.pos - b.pos);
            #endregion

            for (int i = 0; i < tagList.Count; i++)
            {
                Tag tag = tagList[i];
                int depth = 0;
                if (tag.type == TagType.TagStart)
                {
                    for (int j = i + 1; j < tagList.Count; j++)
                    {
                        if (tagList[j].type == TagType.TagEnd && depth == 0)
                        {
                            string innerHtml = pageHtml.Substring(tag.pos + tag.tagStr.Length, tagList[j].pos - tag.pos - tag.tagStr.Length);
                            ForeachTag foreachTag = new ForeachTag(tag.pos, tagList[j].pos, tag.tagStr, tagList[j].tagStr, innerHtml, pageHtml);
                            return foreachTag.pageHtml;
                        }
                        if (tagList[j].type == TagType.TagStart)
                        {
                            depth++;
                        }
                        if (tagList[j].type == TagType.TagEnd)
                        {
                            depth--;
                        }
                    }
                }
            }
            return pageHtml;
        }
        #endregion

    } //end of class TemplateDal

    #region Foreach标签
    /// <summary>
    /// Foreach标签
    /// </summary>
    public class ForeachTag
    {
        /// <summary>
        /// 开始位置 foreach开始标签的开始位置
        /// </summary>
        public int start { get; set; }
        /// <summary>
        /// 结束位置 foreach结束标签的开始位置
        /// </summary>
        public int end { get; set; }
        /// <summary>
        /// 标签字符串
        /// </summary>
        public string tagStr { get; set; }
        /// <summary>
        /// 结束标签字符串
        /// </summary>
        public string tagEndStr { get; set; }
        /// <summary>
        /// 标签内html
        /// </summary>
        public string innerHtml { get; set; }
        /// <summary>
        /// 页面html
        /// </summary>
        public string pageHtml { get; set; }

        /// <summary>
        /// 列表的标识
        /// </summary>
        public string list { get; set; }
        /// <summary>
        /// 列表的model
        /// </summary>
        public string model { get; set; }
        /// <summary>
        /// where
        /// </summary>
        public string where { get; set; }
        /// <summary>
        /// 当面页数
        /// </summary>
        public int page { get; set; }
        /// <summary>
        /// 每页数据条数
        /// </summary>
        public int pageSize { get; set; }

        /// <summary>
        /// 替换结果html
        /// </summary>
        public string resultHtml { get; set; }

        public ForeachTag(int start, int end, string tagStr, string tagEndStr, string innerHtml, string pageHtml)
        {
            this.start = start;
            this.end = end;
            this.tagStr = tagStr;
            this.tagEndStr = tagEndStr;
            this.innerHtml = innerHtml;
            this.pageHtml = pageHtml;

            Regex reg = new Regex(@"([^\s]+)\s*=\s*""([^""]+)""", RegexOptions.IgnoreCase);
            MatchCollection mc = reg.Matches(this.tagStr);
            foreach (Match m in mc)
            {
                switch (m.Groups[1].Value)
                {
                    case "list":
                        this.list = m.Groups[2].Value;
                        break;
                    case "model":
                        this.model = m.Groups[2].Value;
                        break;
                    case "where":
                        this.where = m.Groups[2].Value;
                        break;
                    case "page":
                        this.page = Convert.ToInt32(m.Groups[2].Value);
                        break;
                    case "pageSize":
                        this.pageSize = Convert.ToInt32(m.Groups[2].Value);
                        break;
                }
            }

            switch (list)
            {
                case "channel":
                    StringBuilder sb = new StringBuilder();
                    ChannelDal m_ChannelDal = new ChannelDal();
                    List<cms_channel_ext> channelList = m_ChannelDal.GetList(this.where);
                    foreach (cms_channel_ext channel in channelList)
                    {
                        sb.Append(TemplateDal.ReplaceModels(this.innerHtml, this.model, channel));
                    }
                    this.pageHtml = this.pageHtml.Substring(0, this.start) + sb.ToString() + this.pageHtml.Substring(this.end + this.tagEndStr.Length);
                    break;
                case "content":
                    sb = new StringBuilder();
                    ContentDal m_ContentDal = new ContentDal();
                    PagerModel pager = new PagerModel();
                    pager.rows = this.pageSize;
                    pager.page = this.page;
                    List<cms_content_ext> contentList = m_ContentDal.GetList(ref pager, this.where);
                    foreach (cms_content_ext content in contentList)
                    {
                        sb.Append(TemplateDal.ReplaceModels(this.innerHtml, this.model, content));
                    }
                    this.pageHtml = this.pageHtml.Substring(0, this.start) + sb.ToString() + "<span id=‘totalRows‘ style=‘display:none;‘>" + pager.totalRows + "</span>" + this.pageHtml.Substring(this.end + this.tagEndStr.Length);
                    break;
                default:
                    this.pageHtml = this.pageHtml.Substring(0, this.start) + this.pageHtml.Substring(this.end + this.tagEndStr.Length);
                    break;
            }
        }
    }
    #endregion

    #region 标签类型
    /// <summary>
    /// 标签类型
    /// </summary>
    public enum TagType
    {
        /// <summary>
        /// 标签开始
        /// </summary>
        TagStart = 1,
        /// <summary>
        /// 标签结束
        /// </summary>
        TagEnd = 2
    }
    #endregion

    #region 标签
    /// <summary>
    /// 标签
    /// </summary>
    public class Tag
    {
        /// <summary>
        /// 标签位置
        /// </summary>
        public int pos { get; set; }
        /// <summary>
        /// 标签类型
        /// </summary>
        public TagType type { get; set; }
        /// <summary>
        /// 标签字符串
        /// </summary>
        public string tagStr { get; set; }

        public Tag(int pos, TagType type, string tagStr)
        {
            this.pos = pos;
            this.type = type;
            this.tagStr = tagStr;
        }
    }
    #endregion

} //end of namespace DAL
View Code

在Controller中使用模板引擎:

技术分享
public ActionResult List(int channelId, int page)
{
    PagerModel pager = new PagerModel();
    pager.page = page;
    cms_siteinfo siteInfo = m_SiteInfoDal.Get();
    cms_channel channel = m_ChannelDal.Get(channelId);
    cms_site site = m_SiteDal.Get(channel.siteId);
    cms_content content = m_ContentDal.GetByChannelId(channelId);
    if (content == null)
    {
        return new RedirectResult(string.Format("/site/{0}/error.html", site.folder));
    }

    string templateHtml = string.Empty;
    switch (channel.listType)
    {
        case (int)Enums.ChannelListType.文字列表:
            templateHtml = TemplateDal.LoadTemplate(Server, string.Format("/site/{0}/list.html", site.folder));
            break;
        case (int)Enums.ChannelListType.图片列表:
            templateHtml = TemplateDal.LoadTemplate(Server, string.Format("/site/{0}/list-image.html", site.folder));
            break;
        case (int)Enums.ChannelListType.单篇文章:
            return new RedirectResult(string.Format("/Home/Content?contentId={0}", content.id));
        case (int)Enums.ChannelListType.页面链接:
            return new RedirectResult(string.Format("{3}?page={1}&channelId={2}", site.folder, page, channelId, channel.pageUrl));
    }

    Dictionary<string, object> dic = new Dictionary<string, object>();
    dic.Add("channel", channel);
    dic.Add("siteInfo", siteInfo);
    dic.Add("content", content);
    dic.Add("pager", pager);
    templateHtml = TemplateDal.ReplaceModels(templateHtml, dic);
    while (TemplateDal.HasForeach(templateHtml))
    {
        templateHtml = TemplateDal.ReplaceLists(templateHtml);
    }
    ViewBag.pageHtml = templateHtml;

    return View();
}
View Code

 

以上是关于自己写的简单的模板引擎的主要内容,如果未能解决你的问题,请参考以下文章

js模板引擎原理,附自己写的简洁模板引擎

创建自己的代码片段(CodeSnippet)

JavaScript模板引擎artTemplate.js——结语

freemarker

php模板技术php是怎么向模板中传值的呢?

JavaScript中template模板引擎