将C#对象模板化序列化为JSON

Kee*_*ker 9 .net c# json

我需要将对象序列化为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,NameRelated.Name该对象的.

基本上我正在寻找支持模板JSON序列化方法.

svi*_*ick 5

我不知道任何为您做这件事的图书馆,但是自己构建它并不困难.

如果您有模板,则需要将其解析为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)

您可能希望添加一些错误检查,但除此之外,代码应该可以工作.此外,它使用反射,因此如果性能很重要,它可能不合适.


Kee*_*ker 1

距离我提出这个问题已经过去了 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 模板。在这篇博客中,我还讨论了如何改进这些模板的调试。