json 在C#运行时为已定义的类生成动态字段

polhcujo  于 2023-02-17  发布在  C#
关注(0)|答案(1)|浏览(111)

我有一个类“预测”:

public class Forecast
{
  public int Id { get; set; }
  public int Description{ get; set; }
}

在某些情况下,我为类Forecast添加新字段取决于参数sing反射。因此,返回的对象可能如下所示:

Output 1
{{
  "Id": "1",
  "Description": "Scenario 1",
  "fc": "-45156,60000",
  "fci": "-45156,60000",
  "fcii": null,
  "fciii": null,
  "fciv": null,
}}

Output 2
{{
  "Id": "1",
  "Description": "Scenario 2",
  "fc": "-45156,60000",
  "fci": "-45156,60000",
}}

Output 3
{{
  "Id": "1",
  "Description": "Scenario 3",
  "fc": "-45156,60000",
  "fci": "-45156,60000",
  "fcii": null,
}}

Output 4
{{
  "Id": "1",
  "Description": "Scenario 4",
  "fc": "-45156,60000",
}}

正如你所看到的,在4个场景中有动态字段。我如何将其转换为类预测

**更新:**在运行时创建新字段

public class MyObjectBuilderApi
    {
        public static Type objType { get; set; }

        public MyObjectBuilderApi()
        {
            objType = null;
        }

        /// <summary>
        /// Create new object
        /// </summary>
        /// <param name="Fields">Fields</param>
        /// <returns>Object</returns>
        public static object CreateNewObject(List<FieldApi> Fields,
            string name = "MyDynamicType", Type type = null)
        {
            objType = CompileResultType(Fields, name, type);
            var myObject = Activator.CreateInstance(objType);
            return myObject;
        }

        /// <summary>
        /// Create new type
        /// </summary>
        /// <param name="Fields">Fields</param>
        /// <param name="name">name</param>
        /// <param name="type">type</param>
        /// <returns></returns>
        public static Type CreateNewType(List<FieldApi> fields, string name = "MyDynamicType", Type type = null
            , params AttributeBuilder[] builder)
        {
            return SharedModule.Config.Types.FirstOrDefault(x => x.Name == name) ?? CompileResultType(fields, name, type, builder);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="genericInstance">generic Instance</param>
        /// <param name="sortExpression">sort Expression</param>
        /// <returns></returns>
        public static MethodInfo GetCompareToMethod(object genericInstance,
            string sortExpression)
        {
            Type genericType = genericInstance.GetType();
            object sortExpressionValue = genericType.GetProperty(sortExpression)?
                .GetValue(genericInstance, null);
            Type sortExpressionType = sortExpressionValue?.GetType();
            MethodInfo compareToMethodOfSortExpressionType = sortExpressionType?
                .GetMethod("CompareTo", new Type[] { sortExpressionType });
            return compareToMethodOfSortExpressionType;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Fields">Fields</param>
        /// <param name="name">name</param>
        /// <param name="type">type</param>
        /// <returns></returns>
        public static Type CompileResultType(List<FieldApi> Fields, string name, Type type, params AttributeBuilder[] builder)
        {
            TypeBuilder tb = GetTypeBuilder(type, name);
            tb.DefineDefaultConstructor(MethodAttributes.Public
                                        | MethodAttributes.SpecialName
                                           | MethodAttributes.RTSpecialName);

            // NOTE: assuming your list contains Field objects with fields FieldName(string) and FieldType(Type)
            foreach (var field in Fields)
                CreateProperty(tb, field.FieldName, field.FieldType);

            foreach (var attributeBuilder in builder)
            {
                var displayNameAttributeBuilder = CreateCustomAttribute(attributeBuilder);
                tb.SetCustomAttribute(displayNameAttributeBuilder);
            }
            // I was commented this code because the exception below was occured when lunch solution 21/07/2022
            //'PdAssociatePlanInvoiceTemp', on 'Microsoft.EntityFrameworkCore.DbContextOptions`1[TContext]'
            //violates the constraint of type parameter 'TContext'
            /*ConstructorBuilder myConstructorBuilder = tb.DefineConstructor(MethodAttributes.Public
                                                                           | MethodAttributes.SpecialName
                                                                           | MethodAttributes.RTSpecialName,
                CallingConventions.Standard,new []
                {
                    typeof(DbSet<>)
                        .MakeGenericType( typeof(DbContextOptions<>) .MakeGenericType(tb.UnderlyingSystemType ))
                });
            var baseConstructor = type
                .GetConstructor(BindingFlags.Public | BindingFlags.FlattenHierarchy |
                                BindingFlags.Instance,
                    null, new Type[0], null);

            ILGenerator myConstructorIL = myConstructorBuilder.GetILGenerator();
            myConstructorIL.Emit(OpCodes.Ldarg_0);
            myConstructorIL.Emit(OpCodes.Ldarg_1);
            myConstructorIL.Emit(OpCodes.Call, baseConstructor);
            myConstructorIL.Emit(OpCodes.Nop);
            myConstructorIL.Emit(OpCodes.Nop);
            myConstructorIL.Emit(OpCodes.Ret);*/
            Type objectType = tb.CreateType();
            SharedModule.Config.Types.Add(objectType);
            return objectType;
        }

        private static CustomAttributeBuilder CreateCustomAttribute(AttributeBuilder x)
        {
            if (x.Types == null)
            {
                x.Types = Type.EmptyTypes;
                x.Values = Array.Empty<object>();
            }

            var constructorInfo = x.Attribute.GetConstructor(x.Types);
            var displayNameAttributeBuilder = x.Properties == null ? new CustomAttributeBuilder(constructorInfo, x.Values) :
                new CustomAttributeBuilder(constructorInfo, x.Values,
                    x.Properties
                        .Select(prop => x.Attribute
                            .GetProperty(prop)).ToArray(), x.PropertiesValues,
                    Array.Empty<FieldInfo>(), Array.Empty<object>());
            return displayNameAttributeBuilder;
        }

        private static TypeBuilder GetTypeBuilder(Type type, string name)
        {
            var typeSignature = name;
            AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString()),
                AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
            TypeBuilder tb = moduleBuilder.DefineType(typeSignature
                                , TypeAttributes.Public |
                                TypeAttributes.Class |
                                TypeAttributes.AutoClass |
                                TypeAttributes.AnsiClass |
                                TypeAttributes.BeforeFieldInit |
                                TypeAttributes.AutoLayout
                                , type);
            return tb;
        }

        private static void CreateProperty(TypeBuilder tb, string propertyName,
            string propertyType)
        {
            FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType.GetType(),
                FieldAttributes.Private);
            PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName,
                PropertyAttributes.HasDefault, propertyType.GetType(), null);
            MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName,
                MethodAttributes.Public | MethodAttributes.SpecialName |
                MethodAttributes.HideBySig, propertyType.GetType(), Type.EmptyTypes);
            ILGenerator getIl = getPropMthdBldr.GetILGenerator();

            getIl.Emit(OpCodes.Ldarg_0);
            getIl.Emit(OpCodes.Ldfld, fieldBuilder);
            getIl.Emit(OpCodes.Ret);

            MethodBuilder setPropMthdBldr =
                tb.DefineMethod("set_" + propertyName,
                  MethodAttributes.Public |
                  MethodAttributes.SpecialName |
                  MethodAttributes.HideBySig,
                  null, new[] { propertyType.GetType() });

            ILGenerator setIl = setPropMthdBldr.GetILGenerator();
            Label modifyProperty = setIl.DefineLabel();
            Label exitSet = setIl.DefineLabel();

            setIl.MarkLabel(modifyProperty);
            setIl.Emit(OpCodes.Ldarg_0);
            setIl.Emit(OpCodes.Ldarg_1);
            setIl.Emit(OpCodes.Stfld, fieldBuilder);

            setIl.Emit(OpCodes.Nop);
            setIl.MarkLabel(exitSet);
            setIl.Emit(OpCodes.Ret);

            propertyBuilder.SetGetMethod(getPropMthdBldr);
            propertyBuilder.SetSetMethod(setPropMthdBldr);
        }
    }

然后我用这个生成字段,作为Int(nbForecast=4)的输入

var fields = GenerateFields(nbForecast);

当我创建新对象时,变量newObjectForecast= MyObjectBuilderApi.创建新对象(字段,类型(预测).名称,类型(预测));
所以最后我把字段分配给了我的对象,我的问题是如何从后端链接到返回的json?

qlzsbp2j

qlzsbp2j1#

您可以使用JSON.Net提供的JsonExtensionDataAttribute

var json=@"{
  ""Id"": ""1"",
  ""Description"": ""Scenario 2"",
  ""fc"": ""-45156,60000"",
  ""fci"": ""-45156,60000"",
}";

Forecast forecast = JsonConvert.DeserializeObject<Forecast>(json);

string fci= (string) forecast.Extra["fci"]; // "-45156,60000"

public class Forecast
{
    public int Id { get; set; }
    public string Description { get; set; }
    
    [JsonExtensionData]
    public Dictionary<string, object> Extra { get; set; }
}

也可以将额外的属性放在列表中

public class ForecastExtra : Forecast
{
    [JsonExtensionData]
    private Dictionary<string, object> ExtraDict { get; set; }

    [JsonIgnore]
    public List<KeyValuePair<string, string>> ExtraList
    {
        get
        {
        return ExtraDict?
               .Select(kvp => new KeyValuePair<string, string>(kvp.Key, (string)kvp.Value))
               .Where(kvp => kvp.Value != null)
               .ToList();
        }
    }
}

更新
你不需要任何反射来添加一个条目,只需将其添加到字典中即可.

data.ExtraDict.Add("fcivi","-45158,60001");

但如果你想用更难的方法,用反射

foreach (var prop in data.ExtraList
    {
        var propertyName=prop.Key;
        var propertyValue=(string) prop.Value;
        
        // use reflections to add the property to the class instance
    }

但这和你问题中的json标签无关,它需要一个特殊的帖子。但我不认为你找到了答案,因为它根本没有任何意义。你不能在代码中使用forecast.Extra[“fci”]或forecast.fci,因为你不知道json包含什么属性。

相关问题