Files
ThingsGateway/src/Admin/ThingsGateway.Furion/Templates/Extensions/StringRenderExtensions.cs
2025-10-09 19:05:33 +08:00

167 lines
6.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ------------------------------------------------------------------------
// 版权信息
// 版权归百小僧及百签科技(广东)有限公司所有。
// 所有权利保留。
// 官方网站https://baiqian.com
//
// 许可证信息
// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// ------------------------------------------------------------------------
using System.Text.RegularExpressions;
using ThingsGateway.Extension;
namespace ThingsGateway.Templates.Extensions;
/// <summary>
/// 字符串渲染模板拓展类
/// </summary>
[SuppressSniffer]
public static class StringRenderExtensions
{
/// <summary>
/// 模板正则表达式
/// </summary>
private const string commonTemplatePattern = @"\{(?<p>.+?)\}";
/// <summary>
/// 读取配置模板正则表达式
/// </summary>
private const string configTemplatePattern = @"\#\((?<p>.*?)\)";
/// <summary>
/// 渲染模板
/// </summary>
/// <param name="template"></param>
/// <param name="templateData"></param>
/// <param name="encode"></param>
/// <returns></returns>
public static string Render(this string template, object templateData, bool encode = false)
{
if (template == null) return default;
return template.Render(templateData == null ? default : templateData.ObjectToDictionary().ToDictionary(u => u.Key.ToString(), u => u.Value), encode);
}
/// <summary>
/// 渲染模板
/// </summary>
/// <param name="template"></param>
/// <param name="templateData"></param>
/// <param name="encode"></param>
/// <returns></returns>
public static string Render(this string template, IDictionary<string, object> templateData, bool encode = false)
{
if (template == null) return default;
// 如果模板为空,则跳过
if (templateData == null || templateData.Count == 0) return template;
// 判断字符串是否包含模板
if (!Regex.IsMatch(template, commonTemplatePattern)) return template;
// 获取所有匹配的模板
var templateValues = Regex.Matches(template, commonTemplatePattern)
.Select(u => new
{
Template = u.Groups["p"].Value,
Value = MatchTemplateValue(u.Groups["p"].Value, templateData)
});
// 循环替换模板
foreach (var item in templateValues)
{
template = template.Replace($"{{{item.Template}}}", encode ? Uri.EscapeDataString(item.Value?.ToString() ?? string.Empty) : item.Value?.ToString());
}
return template;
}
/// <summary>
/// 从配置中渲染字符串模板
/// </summary>
/// <param name="template"></param>
/// <param name="encode"></param>
/// <returns></returns>
public static string Render(this string template, bool encode = false)
{
if (template == null) return default;
// 判断字符串是否包含模板
if (!Regex.IsMatch(template, configTemplatePattern)) return template;
// 获取所有匹配的模板
var templateValues = Regex.Matches(template, configTemplatePattern)
.Select(u => new
{
Template = u.Groups["p"].Value,
Value = App.Configuration[u.Groups["p"].Value]
});
// 循环替换模板
foreach (var item in templateValues)
{
template = template.Replace($"#({item.Template})", encode ? Uri.EscapeDataString(item.Value?.ToString() ?? string.Empty) : item.Value?.ToString());
}
return template;
}
/// <summary>
/// 匹配模板值
/// </summary>
/// <param name="template"></param>
/// <param name="templateData"></param>
/// <returns></returns>
private static object MatchTemplateValue(string template, IDictionary<string, object> templateData)
{
string tmpl;
if (!template.Contains('.', StringComparison.CurrentCulture)) tmpl = template;
else tmpl = template.Split('.', StringSplitOptions.RemoveEmptyEntries).First();
var succeed = templateData.TryGetValue(tmpl, out var templateValue);
return ResolveTemplateValue(template, succeed ? templateValue : default);
}
/// <summary>
/// 解析模板的值
/// </summary>
/// <param name="template"></param>
/// <param name="data"></param>
/// <returns></returns>
private static object ResolveTemplateValue(string template, object data)
{
// 根据 . 分割模板
var propertyCrumbs = template.Split('.', StringSplitOptions.RemoveEmptyEntries);
return GetValue(propertyCrumbs, data);
// 静态本地函数
static object GetValue(string[] propertyCrumbs, object data)
{
if (data == null || propertyCrumbs == null || propertyCrumbs.Length <= 1) return data;
var dataType = data.GetType();
// 如果是基元类型则直接返回
if (dataType.IsRichPrimitive()) return data;
object value = null;
// 递归获取下一级模板值
for (var i = 1; i < propertyCrumbs.Length; i++)
{
var propery = dataType.GetProperty(propertyCrumbs[i]);
if (propery == null) break;
value = propery.GetValue(data);
if (i + 1 < propertyCrumbs.Length)
{
value = GetValue(propertyCrumbs.Skip(i).ToArray(), value);
}
else break;
}
return value;
}
}
}