一、环境概述
-
SDK:.NET Core 8.0
-
IDE:Visual Studio 2022
-
项目骨架:标准控制台骨架
-
模板引擎:Handlebars.Net
-
Json处理:Newtonsoft.Json
二、背景概述
项目上需要整理一份参数详细文档,然后参数有的有几百个,有的有一千多个,不可能手动整理,就想到通过结构化模板渲染输出文档。
三、相关依赖
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Handlebars.Net" Version="2.1.6" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
</ItemGroup>
</Project>
四、制作模板
# {{Title}}
[toc]
> 版本: {{Version}}
> 作者: {{Author}}
> 创建时间: {{formatDate CreatedDate}}
> 描述: {{Description}}
---
## 参数详情 (共 {{DetailsCount}} 项)
{{#each Details}}
### {{Sequence}}. {{Name}} (ID: {{Id}})
- **类型**:{{ParamType}}
- **数据类型**:{{DataType}}
**内容**:
{{{Content}}}
**备注**:
{{{Remark}}}
{{/each}}
五、实体和常量
Document.cs
using Newtonsoft.Json;
namespace Online.Admin.Template.Models.DTOs
{
public class Document
{
public Guid Id { get; set; }
public string CriteriaNo { get; set; } = string.Empty;
public string Author { get; set; } = string.Empty;
public DateTime CreatedDate { get; set; }
public string Version { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
[JsonIgnore]
public int DetailsCount => Details?.Count ?? 0;
public List<ParameterInfo> Details { get; set; } = new List<ParameterInfo>();
}
}
ParameterInfo.cs
using Online.Admin.Template.Constant;
using System.Text.Json.Serialization;
namespace Online.Admin.Template.Models.DTOs
{
/// <summary>
/// 表示模板参数的详细信息。
/// </summary>
public class ParameterInfo
{
/// <summary>
/// 参数显示顺序
/// </summary>
public int Sequence { get; set; }
/// <summary>
/// 参数唯一标识符
/// </summary>
public string Id { get; set; } = string.Empty;
/// <summary>
/// 参数编号
/// </summary>
public string Number { get; set; } = string.Empty;
/// <summary>
/// 参数显示名称
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// 参数类型
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public ParameterType ParamType { get; set; } = ParameterType.Direct;
/// <summary>
/// 参数数据类型
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public ParameterDataType DataType { get; set; } = ParameterDataType.Unknown;
/// <summary>
/// 参数内容
/// </summary>
public string Content { get; set; } = string.Empty;
/// <summary>
/// 备注说明
/// </summary>
public string Remark { get; set; } = string.Empty;
}
}
ParameterDataType.cs
namespace Online.Admin.Template.Constant
{
public enum ParameterDataType
{
/// <summary>
/// 小数
/// </summary>
Decimal,
/// <summary>
/// 字符
/// </summary>
String,
/// <summary>
/// 整数
/// </summary>
Integer,
/// <summary>
/// 未知类型
/// </summary>
Unknown
}
}
ParameterType.cs
namespace Online.Admin.Template.Constant
{
public enum ParameterType
{
/// <summary>
/// 字面量参数
/// </summary>
Direct,
/// <summary>
/// 派生参数
/// </summary>
Computed
}
}
SystemConstant.cs
namespace Online.Admin.Template.Constant
{
public class SystemConstant
{
public static readonly string TemplatesBasePath = "XXX\\Templates\\";
public static readonly string JsonDataPath = "XXX\\Templates\\";
}
}
六、数据准备
这里演示就直接通过json反序列化获取实体数据了,就不从数据库读取了
[
{
"sequence": 1,
"id": "user_name",
"number": "P001",
"name": "用户名",
"paramType": "Direct",
"dataType": "String",
"content": "{{UserName}}",
"remark": "从上下文变量中获取"
},
{
"sequence": 2,
"id": "send_time",
"number": "P002",
"name": "发送时间",
"paramType": "Computed",
"dataType": "String",
"content": "System.Now",
"remark": "引用系统当前时间(暂用 String 表示)"
},
{
"sequence": 3,
"id": "is_vip",
"number": "P003",
"name": "是否 VIP",
"paramType": "Direct",
"dataType": "String",
"content": "true",
"remark": "固定值(布尔值暂用 String 表示)"
}
]
七、渲染文档
就是这里要注意的是,有些公司电脑是big5编码字符集的,你发utf8字符集文档过去会乱码,所以生成的时候要注意字符集。
Program.cs
using HandlebarsDotNet;
using Online.Admin.Template.Constant;
using Online.Admin.Template.Models.DTOs;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
var utf8 = Encoding.UTF8;
try
{
var docDataPath = Path.Combine(SystemConstant.JsonDataPath, "Untitled-2.json");
if (!File.Exists(docDataPath))
throw new FileNotFoundException($"JSON 数据文件不存在: {docDataPath}");
string jsonString = File.ReadAllText(docDataPath, utf8);
var jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Converters = { new JsonStringEnumConverter() },
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true
};
var templateData = JsonSerializer.Deserialize<List<ParameterInfo>>(jsonString, jsonOptions)
?? new List<ParameterInfo>();
var document = new Document
{
Id = Guid.NewGuid(),
Author = "张三",
CreatedDate = DateTime.UtcNow,
Version = "1.0.0",
Title = "示例模板标题",
Description = "这是一个用于演示的模板头信息。",
Details = templateData
};
// === 4. 读取模板 ===
var docTempFilePath = Path.Combine(SystemConstant.TemplatesBasePath, "Criteria_Param_Template1.md");
if (!File.Exists(docTempFilePath))
throw new FileNotFoundException($"模板文件不存在: {docTempFilePath}");
string templateContent = File.ReadAllText(docTempFilePath, utf8);
// === 5. 创建 Handlebars 实例并注册 Helper ===
var handlebars = Handlebars.Create(new HandlebarsConfiguration
{
NoEscape = true
});
handlebars.RegisterHelper("formatDate", (writer, context, args) =>
{
if (args.Length > 0 && args[0] is DateTime dt)
{
writer.Write(dt.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"));
}
else
{
writer.Write(string.Empty);
}
});
// === 6. 编译并渲染 ===
var compiledTemplate = handlebars.Compile(templateContent);
string result = compiledTemplate(document);
await File.WriteAllTextAsync("output.md", result, utf8);
Console.WriteLine("output.md 已生成");
}
catch (Exception ex)
{
Console.WriteLine($"处理失败: {ex.Message}");
}
八、效果展示
这里用的MarkText打开的
