我实现了在运行时将“属性”添加到具有特殊SystemComponent.PropertyDescriptor-s的对象的可能性。
由于这些属性只能通过ComponentModel.TypeDescriptor访问,而不能通过Reflection访问,因此这些属性在WPF环境中运行良好,但在序列化时无效。
这是因为我知道所有JSON序列化程序都使用类型的反射。我分析了Newtonsoft.Json,System.Json,System.Web.Script.JavaScriptSerializer,System.Runtime.Serialization.Json。
我认为我不能使用任何这些序列化程序,因为这些序列化程序都不允许修改实例上属性的检索(例如,不能使用ContractResolver)。
有没有办法让JSON序列化与其中一个序列化器一起使用?也许通过特殊配置,覆盖Serializer或类似的某些方法? 是否有其他可用的序列化程序满足此要求?
背景
运行时属性的概念基于this blog entry。
序列化要求来自使用dotNetify序列化视图模型以将它们发送到客户端。
目前,我做了一个dotntify的分支,并通过使用Newtonsoft.Json和递归助手进行部分序列化为序列化做了一个临时的解决方法。 (如果对它感兴趣,你可以看看差异:the Fork)。
答案 0 :(得分:2)
一种可能性是创建https://www.xavierdecoster.com/post/2012/04/26/nuget-version-token-explained.html,在序列化类型为TTarget
的特定对象时,添加一个合成custom ContractResolver
,为指定目标返回{{1}在其对应的IEnumerable<KeyValuePair<Object, Object>>
。
首先,按如下方式定义合同解析器:
DynamicPropertyManager<TTarget>
然后按如下方式序列化您的对象:
public class DynamicPropertyContractResolver<TTarget> : DefaultContractResolver
{
readonly DynamicPropertyManager<TTarget> manager;
readonly TTarget target;
public DynamicPropertyContractResolver(DynamicPropertyManager<TTarget> manager, TTarget target)
{
if (manager == null)
throw new ArgumentNullException();
this.manager = manager;
this.target = target;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
if (objectType == typeof(TTarget))
{
if (contract.ExtensionDataGetter != null || contract.ExtensionDataSetter != null)
throw new JsonSerializationException(string.Format("Type {0} already has extension data.", typeof(TTarget)));
contract.ExtensionDataGetter = (o) =>
{
if (o == (object)target)
{
return manager.Properties.Select(p => new KeyValuePair<object, object>(p.Name, p.GetValue(o)));
}
return null;
};
contract.ExtensionDataSetter = (o, key, value) =>
{
if (o == (object)target)
{
var property = manager.Properties.Where(p => string.Equals(p.Name, key, StringComparison.OrdinalIgnoreCase)).SingleOrDefault();
if (property != null)
{
if (value == null || value.GetType() == property.PropertyType)
property.SetValue(o, value);
else
{
var serializer = JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = this });
property.SetValue(o, JToken.FromObject(value, serializer).ToObject(property.PropertyType, serializer));
}
}
}
};
contract.ExtensionDataValueType = typeof(object);
}
return contract;
}
}
根据需要输出哪些
var obj = new object();
//Add prop to instance
int propVal = 0;
var propManager = new DynamicPropertyManager<object>(obj);
propManager.Properties.Add(
DynamicPropertyManager<object>.CreateProperty<object, int>(
"Value", t => propVal, (t, y) => propVal = y, null));
propVal = 3;
var settings = new JsonSerializerSettings
{
ContractResolver = new DynamicPropertyContractResolver<object>(propManager, obj),
};
//Serialize object here
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
Console.WriteLine(json);
显然,通过将动态属性管理器和目标的集合传递给增强的{"Value":3}
,可以将其扩展为序列化具有动态属性的对象图。只要合同解析器具有从(de)序列化的目标到其{{1}的映射的某种机制,创建合成DynamicPropertyContractResolver<TTarget>
(以及用于反序列化的ExtensionDataGetter
)的基本思想就可以工作。 }。
限制:如果ExtensionDataGetter
类型已有ExtensionDataSetter
成员,则无效。
答案 1 :(得分:1)
感谢dbc的回答,我的解决方案是使用System.ComponentModel.TypeDescriptor的ContractResolver
public class TypeDescriptorContractResolver : DefaultContractResolver
{
public TypeDescriptorContractResolver()
{
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
if (contract.ExtensionDataGetter != null || contract.ExtensionDataSetter != null)
throw new JsonSerializationException(string.Format("Type {0} already has extension data.", objectType));
contract.ExtensionDataGetter = (o) =>
{
return TypeDescriptor.GetProperties(o).OfType<PropertyDescriptor>().Select(p => new KeyValuePair<object, object>(p.Name, p.GetValue(o)));
};
contract.ExtensionDataSetter = (o, key, value) =>
{
var property = TypeDescriptor.GetProperties(o).OfType<PropertyDescriptor>().Where(p => string.Equals(p.Name, key, StringComparison.OrdinalIgnoreCase)).SingleOrDefault();
if (property != null)
{
if (value == null || value.GetType() == property.PropertyType)
property.SetValue(o, value);
else
{
var serializer = JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = this });
property.SetValue(o, JToken.FromObject(value, serializer).ToObject(property.PropertyType, serializer));
}
}
};
contract.ExtensionDataValueType = typeof(object);
return contract;
}
}
我发布了这个,因为它是一种更通用的方法,没有任何依赖于DynamicProperties