我确实写了一个模型生成器来创建客户端ViewModels与服务器模型之间的接口。
一些模型类使用[JsonObject(MemberSerialization.OptIn)]
属性进行序列化,其属性用[JsonProperty ...]
进行序列化,此外,我还有一些自定义属性需要在我的自定义DefaultContractResolver
中进行处理
这是我从任何来源类型创建ModelView的方式
public Type CreateModelView(Type sourceType) {
...
foreach (var attribute in sourceType.CustomAttributes)
typeBuilder.SetCustomAttribute(
new CustomAttributeBuilder(
attribute.Constructor,
attribute.ConstructorArguments.Select(x => x.Value).ToArray()));
...
var fields = GetFields(sourceType);
foreach (var field in fields)
CreateProperty(typeBuilder, field.Name, field.PropertyType, field.GetCustomAttributes(true));
return typeBuilder.CreateTypeInfo().AsType();
}
private void CreateProperty(TypeBuilder typeBuilder, string name, Type type, object[] customAttributes)
{
var fieldBuilder = typeBuilder.DefineField(name, type, FieldAttributes.Public);
var propertyBuilder = typeBuilder.DefineProperty(name, PropertyAttributes.HasDefault, type, null);
var GetMethodBuilder = typeBuilder.DefineMethod(
"Get" + name,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig, type,
Type.EmptyTypes);
var iLGenerator = GetMethodBuilder.GetILGenerator();
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
iLGenerator.Emit(OpCodes.Ret);
var SetMethodBuilder =
typeBuilder.DefineMethod("Set" + name,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null,
new[] { type });
ILGenerator setIl = SetMethodBuilder.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);
foreach (var customAttribute in customAttributes)
AddCustomAttributeToProperty(customAttribute, propertyBuilder);
propertyBuilder.SetGetMethod(GetMethodBuilder);
propertyBuilder.SetSetMethod(SetMethodBuilder);
}
/// <summary>
/// Given a custom attribute and property builder, adds an instance of custom attribute
/// to the property builder
/// </summary>
void AddCustomAttributeToProperty(object customAttribute, PropertyBuilder propBuilder)
{
var customAttributeBuilder = BuildCustomAttribute(customAttribute);
if (customAttributeBuilder != null)
{
propBuilder.SetCustomAttribute(customAttributeBuilder);
}
}
static CustomAttributeBuilder BuildCustomAttribute(object customAttribute)
{
ConstructorInfo longestCtor = null;
// Get constructor with the largest number of parameters
foreach (var cInfo in customAttribute.GetType().GetConstructors().
Where(cInfo => longestCtor == null || longestCtor.GetParameters().Length < cInfo.GetParameters().Length))
longestCtor = cInfo;
if (longestCtor == null)
{
return null;
}
// For each constructor parameter, get corresponding (by name similarity) property and get its value
var args = new object[longestCtor.GetParameters().Length];
var position = 0;
foreach (var consParamInfo in longestCtor.GetParameters())
{
var attrPropInfo = customAttribute.GetType().GetProperty(consParamInfo.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (attrPropInfo != null)
{
args[position] = attrPropInfo.GetValue(customAttribute, null);
}
else
{
args[position] = null;
var attrFieldInfo = customAttribute.GetType().GetField(consParamInfo.Name, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (attrFieldInfo == null)
{
if (consParamInfo.ParameterType.IsValueType)
{
args[position] = Activator.CreateInstance(consParamInfo.ParameterType);
}
}
else
{
args[position] = attrFieldInfo.GetValue(customAttribute);
}
}
++position;
}
var propList = new List<PropertyInfo>();
var propValueList = new List<object>();
foreach (var attrPropInfo in customAttribute.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (!attrPropInfo.CanWrite)
{
continue;
}
object defaultValue = null;
var defaultAttributes = attrPropInfo.GetCustomAttributes(typeof(DefaultValueAttribute), true);
if (defaultAttributes.Length > 0)
{
defaultValue = ((DefaultValueAttribute)defaultAttributes[0]).Value;
}
var value = attrPropInfo.GetValue(customAttribute, null);
if (value == defaultValue)
{
continue;
}
propList.Add(attrPropInfo);
propValueList.Add(value);
}
return new CustomAttributeBuilder(longestCtor, args, propList.ToArray(), propValueList.ToArray());
}
我也使用field.CustomAttributes
测试了此代码,结果是相同的。
propertyBuilder.SetCustomAttribute(
new CustomAttributeBuilder(
attribute.Constructor,
attribute.ConstructorArguments.Select(x => x.Value).ToArray()));
ContractResolver
的结果为空时,问题在attributes
中
public class ContractResolver : DefaultContractResolver {
...
protected override IList<JsonProperty> CreateProperties(
Type type,
MemberSerialization memberSerialization)
{
var propertyList = base.CreateProperties(type, memberSerialization);
foreach (var jProperty in propertyList)
{
var attributes = jProperty.AttributeProvider.GetAttributes(true);
var isBindNever = attributes.OfType<BindNeverAttribute>().Any();
var isKey = attributes.OfType<KeyAttribute>().Any();
...
}
和[JsonProperty ...]
属性不能正常工作。
P.S。我可以从这段代码中获取自定义属性
var PropertyType = jProperty.DeclaringType.GetProperties()
.Where(property => property.Name.ToLower() == jProperty.PropertyName.ToLower())
.FirstOrDefault();
问题是为什么jProperty.AttributeProvider.GetAttributes
无法正常工作,
为什么[JsonProperty]
无法正常工作(标记的属性不会序列化)。
答案 0 :(得分:0)
这可以解决问题,但是效率不高
public class JsonResolver : DefaultContractResolver
{
...
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
var serializableMembers = base.GetSerializableMembers(objectType).Select(memberInfo => memberInfo.Name);
return objectType.GetProperties().Where(memberInfo => serializableMembers.Contains(memberInfo.Name)).Cast<MemberInfo>().ToList();
}
...
}