通过模板字符串和一个匿名对象来解析字符串,这种需求比较常见,对于比较简单的模板字符串,我们可以直接替换,但对于比较复杂的模板,我们可以通过模板工具来实现。
Scriban简介 Scriban 是一个适用于.Net的一款轻量级的脚本引擎。
通过包管理进行安装:
官方案例:
1 2 var template = Template.Parse("Hello {{name}}!" );var result = template.Render(new { Name = "World" });
对于这种简单的模板可以直接使用,但针对嵌套的会出现问题,如:
1 2 3 4 5 6 7 var obj = new { Today = new { time = DateTime.Now, luckyNumber = 3.1415926525 }, }; var temp = Template.Parse("当前时间:{{Today.time}}" ); var test = temp.Render(obj);
针对有嵌套的对象,需要单独对传入的对象进行处理,以下是对该功能的封装
TemplateUtil模板渲染工具 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 public class TemplateUtil { public static string RenderTemplate (string template, object anonymousObject ) { if (string .IsNullOrEmpty(template) || anonymousObject == null ) return template; try { var scriptObject = (ScriptObject)ToScriptObject(anonymousObject); var parsedTemplate = Template.Parse(template); var context = new TemplateContext(); context.PushGlobal(scriptObject); if (parsedTemplate.HasErrors) { return template; } var result = parsedTemplate.Render(context); return result; } catch { return template; } } public static object ToScriptObject (object anonymousObject ) { if (anonymousObject == null ) return null ; if (anonymousObject is JObject jObject) { return ConvertJObjectToScriptObject(jObject); } if (anonymousObject is JToken jToken) { return ConvertJTokenToObject(jToken); } var scriptObject = new ScriptObject(); var type = anonymousObject.GetType(); foreach (var property in type.GetProperties()) { var value = property.GetValue(anonymousObject); scriptObject[property.Name] = ConvertValue(value ); } return scriptObject; } private static ScriptObject ConvertJObjectToScriptObject (JObject jObject ) { var scriptObject = new ScriptObject(); foreach (var property in jObject.Properties()) { scriptObject[property.Name] = ConvertJTokenToObject(property.Value); } return scriptObject; } private static object ConvertJTokenToObject (JToken token ) { if (token == null ) return null ; return token.Type switch { JTokenType.Object => ConvertJObjectToScriptObject((JObject)token), JTokenType.Array => ConvertJArrayToList((JArray)token), JTokenType.Integer => token.Value<long >(), JTokenType.Float => token.Value<double >(), JTokenType.String => token.Value<string >(), JTokenType.Boolean => token.Value<bool >(), JTokenType.Null => null , JTokenType.Date => token.Value<DateTime>(), JTokenType.Guid => token.Value<Guid>(), _ => token.ToString(), }; } private static List<object > ConvertJArrayToList (JArray jArray ) { var list = new List<object >(); foreach (var item in jArray) { list.Add(ConvertJTokenToObject(item)); } return list; } private static object ConvertValue (object value ) { if (value == null ) return null ; var valueType = value .GetType(); if (value is JObject jObject) { return ConvertJObjectToScriptObject(jObject); } if (value is JToken jToken) { return ConvertJTokenToObject(jToken); } if (valueType.IsClass && valueType.Name.Contains("AnonymousType" )) { return ToScriptObject(value ); } if (IsCollection(valueType)) { return ConvertEnumerable(value ); } return value ; } private static bool IsCollection (Type type ) { if (type == typeof (string )) return false ; if (type.IsArray) { return true ; } if (type.IsGenericType) { var interfaces = type.GetInterfaces(); if (interfaces.Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof (IEnumerable<>))) { return true ; } } if (typeof (IEnumerable).IsAssignableFrom(type)) { return true ; } return false ; } private static object ConvertEnumerable (object enumerable ) { var list = new List<object >(); foreach (var item in (IEnumerable )enumerable) { list.Add(ConvertValue(item)); } return list; } }
使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 public static void Test (){ var obj = new { Name = "时分" , Address = new { Province = "四川" , City = "成都" }, Today = new { time = DateTime.Now, luckyNumber = 3.1415926525 }, Scores = new [] { 80 , 85 , 90 }, 爱好 = new { 音乐 = "燕归巢" , 游戏 = "原神" } }; var textTemplate = "你好,我是{{ Name }}。我目前居住在{{ Address['Province'] }}{{ Address.City }},我喜欢听{{ 爱好.音乐 }}这首歌,偶尔玩玩{{ 爱好.游戏 }}" ; var text = TemplateUtil.RenderTemplate(textTemplate, obj); Console.WriteLine(text); var dateTemplate = "当前时间: {{ Today.time | date.to_string '%Y-%m-%d' }}" ; var date = TemplateUtil.RenderTemplate(dateTemplate, obj); Console.WriteLine(date); var number1 = TemplateUtil.RenderTemplate("{{ Today.luckyNumber | math.round 3 }}" , obj); Console.WriteLine(number1); var number2 = TemplateUtil.RenderTemplate("{{ Today.luckyNumber | math.format 'p2'}}" , obj); Console.WriteLine(number2); var scoreTemplate = "分数列表:{{ for score in Scores }} {{ score }} 分 {{ end }}" ; var score = TemplateUtil.RenderTemplate(scoreTemplate, obj); Console.WriteLine(score); var jobj = new JObject { ["Name" ] = "时秒" , ["Address" ] = new JObject { ["Province" ] = "四川" , ["City" ] = "成都" , }, ["座右铭" ] = new JArray { new JObject { ["Sentence" ] = "长风破浪会有时,直挂云帆济沧海" , ["Author" ] = "李白" }, new JObject { ["Sentence" ] = "书中自有颜如玉,书中自有黄金屋" , ["Author" ] = "赵恒" } } }; var textTemplate2 = "你好,我是{{ Name }}。我目前居住在{{ Address['Province'] }}{{ Address.City }}。" + "我特别喜欢{{ for poem in 座右铭 }} {{ poem.Author }}的{{ poem.Sentence }} {{ end }}" ; var text2 = TemplateUtil.RenderTemplate(textTemplate2, jobj); Console.WriteLine(text2); }