[英]Assign values to dynamic number of sub-classes before serializing to JSON
[英]Serializing classes in a dynamic assembly
在运行时,我生成一个动态程序集,其中包含我的应用程序的数据模型。 每个类都用DataContractAttribute
注释,每个属性都用DataMemberAttribute
注释。 现在,如果我尝试序列化对象,则生成的XML仅包含根节点,而没有属性。 但是,如果我在代码中定义了完全相同的类,那么它可以正常工作。 到目前为止,我正在使用“用户”对象进行测试。 这是“静态”实现:
[DataContract]
private class NonDynamicUser
{
[DataMember]
public Guid Id { get; set; }
[DataMember]
public String Username { get; set; }
[DataMember]
public String Password { get; set; }
}
这是“动态”的:
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndCollect);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");
TypeBuilder userTypeBuilder = moduleBuilder.DefineType(assemblyName + "." + "User", TypeAttributes.Public);
Type attrType = typeof(DataContractAttribute);
userTypeBuilder.SetCustomAttribute(new CustomAttributeBuilder(attrType.GetConstructor(Type.EmptyTypes), new object[] { }));
CreateFieldForType(userTypeBuilder, typeof(Guid), "Id");
CreateFieldForType(userTypeBuilder, typeof(String), "Username");
CreateFieldForType(userTypeBuilder, typeof(String), "Password");
Type userType = userTypeBuilder.CreateType();
void CreateFieldForType(TypeBuilder typeBuilder, Type fieldType, String fieldName)
方法基本上创建一个私有字段,然后创建一个带有公共getter和setter的public属性(用DataMemberAttribute
注释)。
我通过反射再次创建了要序列化的实例,这对于“动态”对象和“静态”对象都适用:
object reflectedUser = Activator.CreateInstance(userType);
//object reflectedUser = Activator.CreateInstance(typeof(NonDynamicUser));
reflectedUser.GetType().GetProperty("Id").SetValue(reflectedUser, Guid.NewGuid(), null);
reflectedUser.GetType().GetProperty("Username").SetValue(reflectedUser, "s7orm", null);
reflectedUser.GetType().GetProperty("Password").SetValue(reflectedUser, "s7orm", null);
这就是我序列化对象的方式(直接的东西):
DataContractSerializer ser = new DataContractSerializer(reflectedUser.GetType());
ser.WriteObject(writer, reflectedUser);
这会产生不同的输出,具体取决于使用的是动态还是静态用户类。
你知道我在做什么错吗?
更新:这是CreateFieldForType
方法的代码:
private static void CreateFieldForType(TypeBuilder typeBuilder, Type fieldType, String fieldName)
{
Type attrType = typeof(DataMemberAttribute);
CustomAttributeBuilder attr = new CustomAttributeBuilder(attrType.GetConstructor(Type.EmptyTypes), new object[] { });
FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + fieldName.ToLowerInvariant(), fieldType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(fieldName, PropertyAttributes.HasDefault, fieldType, null);
propertyBuilder.SetCustomAttribute(attr);
MethodAttributes getterAndSetterAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual;
MethodBuilder getMethodBuilder = typeBuilder.DefineMethod("get_" + propertyBuilder.Name, getterAndSetterAttributes, fieldType, Type.EmptyTypes);
ILGenerator getMethodILGenerator = getMethodBuilder.GetILGenerator();
getMethodILGenerator.Emit(OpCodes.Ldarg_0);
getMethodILGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
getMethodILGenerator.Emit(OpCodes.Ret);
MethodBuilder setMethodBuilder = typeBuilder.DefineMethod("set_" + propertyBuilder.Name, getterAndSetterAttributes, null, new Type[] { fieldType });
ILGenerator setMethodILGenerator = setMethodBuilder.GetILGenerator();
setMethodILGenerator.Emit(OpCodes.Ldarg_0);
setMethodILGenerator.Emit(OpCodes.Ldarg_1);
setMethodILGenerator.Emit(OpCodes.Stfld, fieldBuilder);
setMethodILGenerator.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getMethodBuilder);
propertyBuilder.SetSetMethod(setMethodBuilder);
}
将模块名称更改为与assembly / dll名称相同,使我能够在Reflector中查看类:
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("blah.dll");
我创建了自己的CreateFieldForType方法版本,并且对我有用。 这是确定正确生成类型的方法:
userInstance.GetType().GetProperty("Id").SetValue(userInstance, Guid.NewGuid(), null);
userInstance.GetType().GetProperty("Username").SetValue(userInstance, "Test1", null);
userInstance.GetType().GetProperty("Password").SetValue(userInstance, "Test2", null);
MemoryStream stream = new MemoryStream();
DataContractSerializer ser = new DataContractSerializer(userInstance.GetType());
ser.WriteObject(stream, userInstance);
stream.Seek(0, SeekOrigin.Begin);
object reSerialized = ser.ReadObject(stream);
foreach (var property in reSerialized.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
Console.WriteLine("{0}: {1}", property.Name, property.GetValue(reSerialized, null));
我采用了您刚刚发布的方法,并针对我的代码运行了该方法。 没用 然后,我慢慢用我创建的代码替换它。 我发现破坏您代码的行(使我的验证代码停止工作)是此行:
MethodAttributes getterAndSetterAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual;
删除MethodAttributes.Virtual
标志后,代码的工作原理与NonDynamicUser相同。
我建议您使生成的程序集正常工作,以便可以在反射器中看到生成的类。 这将使验证进一步的修改变得更加容易(例如,您可以添加virtual关键字)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.