使用反射将模板占位符替换为对象属性

Replace template Placeholder with Object Properties with Reflection

在 C# 中,我想使用反射将字符串占位符替换为对象属性

        string formula = "{\"Name\": \"{{Name}}\", \"Email\": \"{{Email}}\" }";
        Student student = new Student();
        student.Name = "Parker";
        student.Email = "Parker@xyz.com";
        student.Address = "Mark Avenue";
        var result1 = GenerateJson(formula, student);
        //Output :  "{\"Name\": \"Parker\", \"Email\": \"Parker@xyz.com\" }"

        student.Name = "Royal";
        student.Email = "Royal@xyz.com";
        student.Address = "Cross Lane";
        var result2 = GenerateJson(formula, student);
        //Output :  "{\"Name\": \"Royal\", \"Email\": \"Royal@xyz.com\" }"





    public string GenerateJson(string formula, Student student)
    {
        string result = "";
        //logic for replacing the Placeholder woth object properties
        return result;
    }

    class Student
    {
        public string Name { get; set; }
        public string Email { get; set; }
        public string Address { get; set; }

    }

您可以将其反序列化为 ExpandoObject (IDictionary<string,object>)。然后将 属性 个名称与已知类型进行比较。如果词典的键和学生的 属性姓名匹配。将 ExpandoObject 的值替换为 Student 的 属性 的值。毕竟序列化为json.

在这里,

public string GenerateJson(string formula, Student student)
    {
        IDictionary<string, object> templateValues = JsonConvert.DeserializeObject<IDictionary<string, object>>(formula);

        PropertyInfo[] sourceProperty = typeof(Student).GetProperties();

        foreach (var item in sourceProperty)
        {
            KeyValuePair<string,object> value = templateValues.FirstOrDefault(x=> x.Key == item.Name);

            if (value.Key != null)
            {
                templateValues[item.Name] = item.GetValue(student);
            }
        }

        return JsonConvert.SerializeObject(templateValues);
    }

如果您真的不想或不能使用 Json.NET,您可以尝试下面的解决方案

public string GenerateJson(string formula, Student student)
{
    return Regex.Replace(formula, @"\{\{(\w+)\}\}", match => typeof(Student).GetProperty(
        match.Groups[1].ToString())?.GetValue(student)?.ToString());
}

看起来实际问题是检索特定属性的值以生成 API 签名。尚不清楚签名是否真的需要 JSON 字符串。

最简单的方法是创建一个具有必要属性的匿名类型并将其序列化,例如:

var payload=JsonConvert.Serialize(new {student.Name,student.Email});

这比任何反射代码都快得多,并且只分配一个额外的对象。如果要将 API 与 lot 的不同请求类型一起使用,则需要使用代码生成器或在 C# 9 中使用源代码生成器来生成此类调用。

可以(但很慢)使用反射来检索特定属性,例如:

var dict=typeof(Student).GetProperties()
                   .Where(prop=>myProps.Contains(prop.Name))
                   .ToDictionary(prop=>prop.Name,prop=>prop.GetValue(student));
var json=JsonConvert.Serialize(dict);

JSON 对象实际上是一个字典,因此序列化字典的行为类似于序列化具有相同属性的对象。

尽管反射相对昂贵,所以缓存您想要的 PropertyInfo 对象并重用它们是个好主意:

Dictionary<Type,PropertyInfo[]> _properties=new Dictionary<Type,PropertyInfo[]>();

...
string GenerateJson<T>(T item)
{
    PropertyInfo[] props;
    if (!_properties.TryGetValue(typeof(T),out props))
    {
        props=typeof(Student).GetProperties()
                   .Where(prop=>myProps.Contains(prop.Name))
                   .ToArray();
    }

    var dict=props.ToDictionary(prop=>prop.Name,prop=>prop.GetValue(item));
    return JsonConvert.Serialize(dict);
}