我需要将对象序列化为JSON.我想用模板而不是使用数据注释(大多数框架都这样做).有人知道这样做的好方法吗?
一张图片说的超过1000个单词.我正在寻找看起来像这样的东西:

例如,如果我有这样的类:
public class Test
{
public string Key { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public Test Related { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
而一个有模板字符串,可能是这样的:
{
id: "$Key",
name: "$Name",
related: "$Related.Name"
}
Run Code Online (Sandbox Code Playgroud)
我希望得到一个JSON对象,其属性都充满了根据Key,Name而Related.Name该对象的.
基本上我正在寻找支持模板的JSON序列化方法.
我不知道任何为您做这件事的图书馆,但是自己构建它并不困难.
如果您有模板,则需要将其解析为JSON,然后用实际值替换所有占位符.为此,您可以使用访问者模式.
由于JSON.NET(我正在使用的JSON库)似乎没有访问者,您可以自己创建一个:
abstract class JsonVisitor
{
public virtual JToken Visit(JToken token)
{
var clone = token.DeepClone();
return VisitInternal(clone);
}
protected virtual JToken VisitInternal(JToken token)
{
switch (token.Type)
{
case JTokenType.Object:
return VisitObject((JObject)token);
case JTokenType.Property:
return VisitProperty((JProperty)token);
case JTokenType.Array:
return VisitArray((JArray)token);
case JTokenType.String:
case JTokenType.Integer:
case JTokenType.Float:
case JTokenType.Date:
case JTokenType.Boolean:
case JTokenType.Null:
return VisitValue((JValue)token);
default:
throw new InvalidOperationException();
}
}
protected virtual JToken VisitObject(JObject obj)
{
foreach (var property in obj.Properties())
VisitInternal(property);
return obj;
}
protected virtual JToken VisitProperty(JProperty property)
{
VisitInternal(property.Value);
return property;
}
protected virtual JToken VisitArray(JArray array)
{
foreach (var item in array)
VisitInternal(item);
return array;
}
protected virtual JToken VisitValue(JValue value)
{
return value;
}
}
Run Code Online (Sandbox Code Playgroud)
然后创建一个专门的访问者,用实际值替换占位符:
class JsonTemplateVisitor : JsonVisitor
{
private readonly object m_data;
private JsonTemplateVisitor(object data)
{
m_data = data;
}
public static JToken Serialize(object data, string templateString)
{
return Serialize(
data, (JToken)JsonConvert.DeserializeObject(templateString));
}
public static JToken Serialize(object data, JToken template)
{
var visitor = new JsonTemplateVisitor(data);
return visitor.Visit(template);
}
protected override JToken VisitValue(JValue value)
{
if (value.Type == JTokenType.String)
{
var s = (string)value.Value;
if (s.StartsWith("$"))
{
string path = s.Substring(1);
var newValue = GetValue(m_data, path);
var newValueToken = new JValue(newValue);
value.Replace(newValueToken);
return newValueToken;
}
}
return value;
}
private static object GetValue(object data, string path)
{
var parts = path.Split('.');
foreach (var part in parts)
{
if (data == null)
break;
data = data.GetType()
.GetProperty(part)
.GetValue(data, null);
}
return data;
}
}
Run Code Online (Sandbox Code Playgroud)
用法很简单.例如,使用以下模板:
{
id : "$Key",
name: "$Name",
additionalInfo:
{
related: [ "$Related.Name" ]
}
}
Run Code Online (Sandbox Code Playgroud)
你可以使用这样的代码:
JsonTemplateVisitor.Serialize(data, templateString)
Run Code Online (Sandbox Code Playgroud)
结果如下所示:
{
"id": "someKey",
"name": "Isaac",
"additionalInfo": {
"related": [
"Arthur"
]
}
}
Run Code Online (Sandbox Code Playgroud)
您可能希望添加一些错误检查,但除此之外,代码应该可以工作.此外,它使用反射,因此如果性能很重要,它可能不合适.
距离我提出这个问题已经过去了 10 年。由于我一直在使用 Node.JS 并发现了 Handlebars 以及如何轻松地让它解析 JSON 而不是 HTML 模板。Handlebars 项目已转换为 .NET。
您可以使用特殊的方法ITextEncoder让 Handlebars 生成 JSON:
using HandlebarsDotNet;
using System.Text;
public class JsonTextEncoder : ITextEncoder
{
public void Encode(StringBuilder text, TextWriter target)
{
Encode(text.ToString(), target);
}
public void Encode(string text, TextWriter target)
{
if (text == null || text == "") return;
text = System.Web.HttpUtility.JavaScriptStringEncode(text);
target.Write(text);
}
public void Encode<T>(T text, TextWriter target) where T : IEnumerator<char>
{
var str = text?.ToString();
if (str == null) return;
Encode(str, target);
}
}
Run Code Online (Sandbox Code Playgroud)
让我们看看它的实际效果:
using HandlebarsDotNet;
var handlebars = Handlebars.Create();
handlebars.Configuration.TextEncoder = new JsonTextEncoder();
var sourceTemplate = @"{
""id"": ""{{Key}}"",
""name"": ""{{Name}}"",
""related "": ""{{Related.Name}}""
}";
var template = handlebars.Compile(sourceTemplate);
var json = template(new
{
Key = "Alpha",
Name = "Beta",
Related = new
{
Name = "Gamme"
}
});
Console.WriteLine(json);
Run Code Online (Sandbox Code Playgroud)
这将写入以下内容:
{
"id": "Alpha",
"name": "Beta",
"related ": "Gamme"
}
Run Code Online (Sandbox Code Playgroud)
我在博客上写了一篇关于该主题的小文章:Handlebars.Net 和 JSON 模板。在这篇博客中,我还讨论了如何改进这些模板的调试。