轻量级html模板渲染库cJinja

教育动态2024-09-15 19:06:56匿名

cJinja是一个用cpp编写的轻量级HTML模板解析库,依靠ejson实现模板数据替换(在jinja中称为context)。模板的语法与Django Jinja基本相同,功能相当丰富。源码只有700行,适合学习。如果你觉得不错,就给个star吧。

(本程序是https://github.com/HuangHongkai/tinyserver中的一个模块)

编译

使用cmake编译,windows和linux下都可以编译。推荐使用clion作为IDE。

编译成功后,build目录下会出现libcjinja.a和cjinja_test.exe两个文件。 libcjinja.a是一个静态库,cjinja_test.exe是一个简单的测试程序。

运行测试程序后,会出现output.html(该文件是解析tmp.html的结果。)

已完成的功能

变量,如{{ var }}变量索引访问,如{{ var.var2 }} {{ var[2] }} {{ var[2].key.value[2] }},其中**[ ]* * 表示对数组进行索引(类似于python的列表),表示索引一个对象(类似于python的dict)表达式计算(包括字符串拼接),如{{ 1*1+2-3*var }} {{ 1+1*2-3/4 }} {{ 'asdfsf '+var }}for-endfor 迭代列表,例如{% for var in list %} {% endfor %}for-endfor 迭代对象迭代,例如{% for key,value in object %} { % endfor %} 或{% for key in object %}{% endfor %} 或{% for ,value in object %} {% endfor %}if-else -endif 语句,其中if 条件支持四种算术运算,简单比较(!===)等,例如{% if 1+1==2 %}aaa{% else %}bbb{%endif %}模板包含,嵌套其他模板文件时{% include 'other .html' %} 模板语法错误信息,请注意表达式之间不能有空格。例如,{{ 1 + 1 }} 是非法的,而{{ 1+1 } } 是合法的。

轻量级html模板渲染库cJinja

如何使用

1. 变量和变量索引

一个简单的例子如下:

HtmlTemplate html('username:{{ 用户名}}\n' 'parm.list[1][2]: {{parm.list[1][2] }} \n' 'parm.key: {{ parm.key } }', 1); //参数1表示传入模板字符串,0表示传入文件名,默认为0JSONObject obj={ {'username', 1234}, {'parm', { {'key', ' cde'}, {'list', {1, {1,2.3, 'abcd'}, '哈哈哈'}}, }}};html.setValue(obj);cout html.render() endl endl;/*运行后打印如下: username:1234parm.list[1]: abcd parm.key: cde*/HtmlTemplate 是一个库的主类,构造函数为

显式HtmlTemplate(const string str, int flag=0); //flag=0表示str代表文件路径,不为0则表示传入模板字符串

str参数是一个字符串,它可以代表HTML模板的原始字符串或文件的路径。该标志默认为0。

setValue方法的意思是向模板对象传入数据。

render() 方法表示将模板解析为字符串。

轻量级html模板渲染库cJinja

2. 列表迭代

HtmlTemplate html('{% for x in list %}{{ x }}\n{%endfor%}' '此时x已经是临时变量,无法打印{{x}}\n' , 1 ) ;JSONObject obj=OBJECT( KEYVALUE('list', LIST(1,2,3,4,5)));cout html.setValue(obj).render() endl endl;/*运行后输出如下12345 this 此时x已经是临时变量,无法打印。 */注意,迭代过程中,x作为临时变量,不能外部打印。

3. 字典迭代

HtmlTemplate html('{% for key in dict %}迭代1: 字典的键值为{{ key }}\n{% endfor %}' '{% for key,value in dict %}迭代2: 键值字典的值为{{ key }},值为{{ value}}\n{% endfor %}' '{% for ,value in dict %}迭代3: 字典的值为{{ value }}\n {% endfor % }', 1);JSONObject obj=OBJECT( KEYVALUE('dict', OBJECT( KEYVALUE('key1', 'value1'), KEYVALUE('key2', 1234), KEYVALUE('key3', nullptr ), )) );cout html.setValue(obj).render() endl endl;/*运行后输出为迭代1:字典的键值为key1迭代1:字典的键值为key2迭代1:字典的键值为key3 迭代2:字典的键值为key1,值为value1,迭代2:字典的键值为key2,值为1234,迭代2:字典的key值为key3,值为null,迭代3:字典的值为value1,迭代3:字典的值为1234 迭代3: 字典的值为null*/4。字符串连接和表达式计算

HtmlTemplate html('{{ a+b+c+\'444\' }}\n' '{{x}} * {{y}} + 2 * 3 - 4/{{x}}={{ x* y+2*3-4/x }}\n', 1);JSONObject obj=OBJECT( KEYVALUE('a', '111'), KEYVALUE('b', '222'), KEYVALUE('c' , '333'), KEYVALUE('x', 12), KEYVALUE('y', 34) );cout html.setValue(obj).render() endl endl;/*运行后输出11122233344412 * 34 + 2 * 3 - 4/12=413.667*/5。 if-else-endif 语句

HtmlTemplate html('{% if 1==1 %} 1==1 为真{% else %} 1==1 不为真{%endif %}\n' '{% if !x %} x 为空{% else %} x不为空{%endif %}\n' '{% if x==2 %} x==2成立{% endif %}\n' '{% if x+1!=2 %} x +1!=2 成立{% endif %}\n' '{% if x3 %} x3 成立{% endif %}\n' '{% if x1 %} x1 成立{% endif %}\ n' '{ % if str==\'abcd\' %} str is abcd {% endif %}\n' '{% if 1 %} 常量表达式1 {% endif %}\n' '{% if 0 %} 常量表达式0, {%endif%}', 1) 这里不会输出; JSONObject obj={ {'x', 2}, {'str', 'abcd'}};cout html.setValue(obj). render() endl;/*运行后输出1==1。确定x不为空。 x==2。成立x+1!=2。可知x3成立。设定用途

HtmlTemplate html('{%for x in list%}' '{%if x %}' '{% for y in list2%}' '{{x}} * {{y}}={{ x*y } }\n' '{% endfor %}' '{% else %}' 'x 的值为空\n' '{%endif%}' '{% endfor%}', 1);JSONObject obj=OBJECT ( KEYVALUE('list', LIST(1,2,3,4,5)), KEYVALUE('list2', LIST(1,2,3)),);cout html.setValue(obj).render() endl endl;/*运行后输出1 * 1=11 * 2=21 * 3=32 * 1=22 * 2=42 * 3=63 * 1=33 * 2=63 * 3=94 * 1=44 * 2=84 * 3=125 * 1=55 * 2=105 * 3=15*/7。作为输出的模板文件

HtmlTemplate html('tmpl.html');JSONObject context=OBJECT( .);FILE* f=fopen('output.html', 'w'); //写入文件string str=html.setValue(context).render();fwrite(str.c_str(), 1, str.size(), f);fclose(f);/*运行后,打开当前目录下的tmpl.html文件作为输入输出文件为output.html*//*如果tmpl.html不存在,则会抛出异常*/8.异常处理

轻量级html模板渲染库cJinja

HtmlTemplate html('{% if 1 %} xxx ', 1);//不传入contexttry { cout html.render() endl;} catch(exception e) { cerr e.what() endl;}cout endl ;运行后,终端打印如下:

它会提示异常类名、异常文件的位置、代码行数以及一些错误信息。

讨论

1. 实现简单表达式计算器的更好方法是什么? (例如,{{ 2.3*3+4/5*x }} 等表达式)

第一步是提取数据和符号并将其放入数组中,并将所有输入类型设置为double。例如,在上面的表达式中,提取的符号为{*,/,*},提取的数据为{2.3,3,4,5,x}。这一步位于__parse_var函数中,比较简单,不再详细讨论。第二步,首先计算乘法和除法,将结果放入栈中,然后计算栈中元素的加法和减法(按照我们通常计算表达式的方式,先乘除再加和减去)。这一步的实现如下(使用了C语言宏和C++11匿名函数) double cJinja:HtmlTemplate:calculator(vectorany number, vectorchar op) { //例如下面的表达式将变成//1 - 2 - 3 + 2 * 3 * 4 - 4*5 //矢量字符op={ '-', '-', '+', '*', '*', '-', '*' }; //任意向量={ 1, 2, 3, 2, 3, 4, 4, 5 }; if (number.size() !=op.size() + 1) throwException(TemplateParseException, '操作符号个数与操作数不匹配'); /* 定义计算器的内部函数*/auto calc=[ ](any var1, double var2, char op) - double{ //var2 + var1 //var2 * var1 //var2 - var1 //var2/var1//注意顺序#define CALC(op2) \ if(#op2[ 0]==op) { \ if (var1.type()==typeid(int)) \ return var2 op2 static_castdouble(any_castint(var1) ); \ else if (var1.type()==typeid(float)) \ return var2 op2 static_castdouble(any_castfloat(var1)) ; \ else if (var1.type()==typeid(double)) \ return var2 op2 static_castdouble(any_castdouble(var1)) ; \ } 计算(+);计算(-);计算(*);计算(/); throwException(TemplateParseException, '不允许对空指针进行操作'); #undef 计算};矢量双num_stack; //计算中间结果存储栈num_stack.push_back(calc(number[ 0], 0, '+')); //获取值number[i+1] + 0(加法运算的零元素为0,乘法运算的零元素为1) /* 计算*/方法*/for (size_t i=0; i op.size(); i++) { if (op[i]=='+' || op[i]=='-') { num_stack.push_back(calc(number[i + 1], 0, ' +')); //number[i+1] + 0 } else if (op[i]=='*' || op[i]=='/') { double var1=num_stack.back (); num_stack.pop_back(); num_stack.push_back(calc(number[i + 1], var1, op[i])); //var1/number[i+1] 或var1/number[i+1 ] } else throwException(TemplateParseException, str_format('非法运算符%d', op[i])); } /* 计算+ - 方法*/double result=num_stack[0];大小_t i=1; for (auto ch : op) { if (ch=='+') { 结果+=num_stack[i++]; } else if(ch=='-') { result -=num_stack[i++]; } } 返回结果;}2.抛出异常并提供更多信息

用户评论

巷雨优美回忆

这个cJinja库听起来真的很不错,用起来简单又方便。我之前也一直在找这样的库,希望能提高我的开发效率。

    有15位网友表示赞同!

百合的盛世恋

用了cJinja之后,发现模板渲染真的快了不少,特别适合做页面快速迭代的项目。

    有7位网友表示赞同!

心贝

我看了一下cJinja的文档,感觉功能挺全面的,不过不知道性能方面如何,有没有人实测过呢?

    有19位网友表示赞同!

孤独症

我尝试了一下cJinja,发现它的语法确实简洁,但不知道为什么我渲染出来的页面有点问题,有人遇到过类似的情况吗?

    有13位网友表示赞同!

龙卷风卷走爱情

cJinja这个库我用了好几个项目了,真的很好用,特别推荐给那些做前端的同学。

    有19位网友表示赞同!

心安i

轻量级的库总是让人喜欢,cJinja体积小,功能齐全,简直是开发者的福音。

    有13位网友表示赞同!

服从

看了cJinja的源码,发现它对安全性的处理也很到位,这个库真的让人放心。

    有12位网友表示赞同!

封心锁爱

我最近在用cJinja,感觉它的模板引擎比其他库都要灵活,可以轻松实现各种复杂的布局。

    有7位网友表示赞同!

病房

cJinja的模板渲染速度真的很惊喜,比之前用的库快了不少,工作效率提高了不少。

    有8位网友表示赞同!

把孤独喂饱

不过我觉得cJinja的文档可以更加详细一些,对于初学者来说可能有些难以理解。

    有6位网友表示赞同!

tina

用了cJinja之后,发现它的一些高级功能有点复杂,不太容易上手。

    有10位网友表示赞同!

墨染年华

cJinja的扩展性也很不错,可以方便地集成其他库,这是我比较看重的一点。

    有20位网友表示赞同!

歆久

虽然cJinja是轻量级的,但我感觉它在兼容性上还是有点问题,有时候渲染出来的页面会有兼容性问题。

    有14位网友表示赞同!

落花忆梦

听说cJinja的开发者社区很活跃,有问题可以随时提问,这对我来说是个很大的吸引力。

    有9位网友表示赞同!

请在乎我1秒

cJinja的模板语法确实简洁,但我觉得它的一些内置函数功能还不够强大,希望未来能有所改进。

    有19位网友表示赞同!

一样剩余

我尝试了几个轻量级模板渲染库,最终选择了cJinja,真的没让我失望。

    有13位网友表示赞同!

惯例

cJinja这个库我给满分,用起来真的太方便了,强烈推荐给所有前端开发者。

    有12位网友表示赞同!

傲世九天

虽然cJinja有优点,但有时候它的错误提示不是很明确,让人有点摸不着头脑。

    有15位网友表示赞同!

妄灸

总体来说,cJinja是个不错的库,不过在使用过程中还是要注意细节,避免出现不必要的麻烦。

    有13位网友表示赞同!

相关推荐