开发者

Template Parsing and Reflection

I'm writing a content management system in ASP.NET/C# which the template of the site is defined in a html file. I am passing parameters to the html file with blocks. A block starts with [ and ends with ]. An example for a template with some simple blocks:

<html>
<head>
    <title>[Title]</title>
    <meta name="description" content="[Description]" />
</head>
<body>
    <h1>[Headline]</h1>
    <p>[Text]</p>
</body>  
</html>

Now, I've a class which represents the template. The class for the example template looks like this:

public class MyTemplate
{
    public string Title
    {
        get;
        set;
    }

    public string Description
    {
        get;
        set;
    }

    public string Headline
    {
        get;
        set;          
    }

    public string Text
    {
        get;
        set;          
    }
}

The class could be a class that I write, a class generated by Linq To SQL or just any class.

I created a method which replaces a block with the value of the property. I'm using regular expression for this purpose:

    public static string ParseTemplateFromObject(string input, object obj)
    {
        return Regex.Replace(input, @"\[(.*?)\]", new MatchEva开发者_开发知识库luator(delegate(Match match)
        {
            var blockName = match.Result("$1");

            return obj.GetType().GetProperties().SingleOrDefault(p => p.Name.Equals(blockName, StringComparison.OrdinalIgnoreCase))
                .GetValue(obj, null).ToString();

        }), RegexOptions.Multiline);
    }

...and it is working. I'm using GetProperties() and then Linq instead of GetProperty to prevent case sensitivity. But now I've another problem, when I want to use parameters in a block. For example: I want to create a vertical menu. The menu in the system can be vertical or horizontal:

[Menu Id=1, Direction="Vertical"]

So, I decided that this type of block calls a method and extracts it's value, instead of extracting the value from it's property. Example:

public class MyTemplate
{
    ...
    public string Menu(int id, string direction)
    {
        string menu = ...;
        return menu;
    }
}

I extended my ParseTemplateFromObject to support this:

    public static string ParseTemplateFromObject(string input, object obj)
    {
        return Regex.Replace(input, @"\[(.*?)\]", new MatchEvaluator(delegate(Match match)
        {
            var blockName = match.Result("$1");

            var m = Regex.Match(blockName, "(?<Name>\\w+)=((?<Value>\\w+)|\"(?<Value>([^\"]*))\")");

            if (m.Captures.Count > 0)
            {
                var method = obj.GetType().GetMethods().Single(p => p.Name.Equals(blockName.Substring(0, blockName.IndexOf(' '))
                    , StringComparison.OrdinalIgnoreCase));

                var methodParameters = method.GetParameters();
                var parameters = new object[methodParameters.Length];

                while (m.Success)
                {
                    var name = m.Groups["Name"].Value;
                    var value = m.Groups["Value"].Value;

                    var methodParameter = methodParameters.Single(p => p.Name.ToLower() == name.ToLower());
                    parameters[methodParameter.Position] =
                        Convert.ChangeType(value, methodParameter.ParameterType);

                    m = m.NextMatch();
                }

                return method.Invoke(obj, parameters).ToString();
            }
            else
            {
                return obj.GetType().GetProperties().SingleOrDefault(p => p.Name.Equals(blockName, StringComparison.OrdinalIgnoreCase))
                    .GetValue(obj, null).ToString();

            }
        }), RegexOptions.Multiline);
    }
}

It's working, but I'm looking for ways to make it more effective. And I don't know if it is actually the right way to implement this?

Thank you.


Perhaps use the native templating provided by ASP.Net? I.e, define your templates using standard .aspx pages and then use the Server.Execute method to capture the output generated by these pages. You will be able to use server codeblocks (<%= %>) to either 1.) Insert variables exposed by the code behind or 2.) execute methods in the code behind. Would save you the effort of updating your parsing code and will provide an widely recognised template format.


Have you considered managing the styling of the tokens in the actual template as opposed to styling them from within the code? For instance you have a direction property that might be an ideal candidate for my suggested approach. That way you can keep the parsing of tokens very simple.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜