gpt4 book ai didi

c# - IDynamicMetaObjectProvider 实现反序列化类的 Newtonsoft 问题

转载 作者:行者123 更新时间:2023-11-30 23:15:29 25 4
gpt4 key购买 nike

目前我偶然发现了 Newtonsofts Json 库的一个问题,这对我来说完全是个谜。

我有几个类正在实现 IDynamicMetaObjectProvider 接口(interface)。将多个对象序列化为 json 没有问题,我从对象的每个实例中得到了我期望的 json。

但是反序列化让我很头疼。从我目前观察到的情况来看,该库似乎正在缓存它找不到的每个动态属性的值,并在应用程序运行时保留它。例如,我有以下三个 Json:

{ "PropA": "1" }
{ "PropA": "2", "PropB": "1" }
{ "PropA": "3", "PropB": "2", "PropC": "1" }

连续反序列化 Jsons 将得到以下 .NET 对象:

{ "PropA": "1" }
{ "PropA": "1", "PropB": "1" }
{ "PropA": "1", "PropB": "1", "PropC": "1" }

然而!如果我将目标类型从实现 IDynamicMetaObjectProvider 的类型更改为 Dicitionary 或简单的动态,反序列化的对象将正确设置属性。

我的类有一个索引属性,在 setter 上设置了一个断点,已经为 setter 提供了错误的值(所以我的类的实现没有问题)。

public abstract class DynamicModelObject : IDynamicMetaObjectProvider //, IPropertyIndexer //, IDictionary<String, Object>
{
[NotMapped]
[JsonIgnore]
internal Dictionary<String, Object> properties = new Dictionary<String, Object>();

[IgnoreProperty]
[JsonIgnore]
public override Object this[String propertyName]
{
get
{
object val;

if (properties.TryGetValue(propertyName, out val)) {
return val;
}

var prop = this.GetType().GetProperty(propertyName);
if (prop != null && prop.CanRead) {
return prop.GetValue(this);
}
return null;
}

set
{
isDearty = true;
var prop = this.GetType().GetProperty(propertyName);
if (prop != null && prop.CanWrite) {
prop.SetValue(this, value);
} else {
properties[propertyName] = value;
}

var val = value as String;
if (value == null || (val != null && String.IsNullOrEmpty(val))) {
properties.Remove(propertyName);
}
}
}

public DynamicMetaObject GetMetaObject(System.Linq.Expressions.Expression parameter)
{
return new DynamicDictionaryPropertyStore<DynamicModelObject>(parameter, this);
}

[IgnoreProperty]
public IEnumerable<String> DynamicPropertyMemberNames
{
get
{
foreach (var key in properties.Keys) {
yield return key;
}
}
}

private List<String> staticProperties = null;

[IgnoreProperty]
private IEnumerable<String> StaticPropertyMemberNames
{
get
{
if (staticProperties == null) {
staticProperties = new List<String>();
foreach (var prop in this.GetType()
.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty)) {
if (!Attribute.IsDefined(prop, typeof(IgnorePropertyAttribute)) && !Attribute.IsDefined(prop, typeof(ScriptIgnoreAttribute))) {
staticProperties.Add(prop.Name);
yield return prop.Name;
}
}
} else {
foreach (var prop in staticProperties) {
yield return prop;
}
}
}
}

[IgnoreProperty]
private IEnumerable<String> AllPropertyMemberNames
{
get
{
foreach (var prop in DynamicPropertyMemberNames.Concat(StaticPropertyMemberNames)) {
yield return prop;
}
}
}

private class DynamicDictionaryPropertyStore<T> : DynamicMetaObject where T : DynamicModelObject
{
private T target;

internal DynamicDictionaryPropertyStore(System.Linq.Expressions.Expression parameter, T target)
: base(parameter, BindingRestrictions.Empty, target)
{
this.target = target;
}

public override IEnumerable<string> GetDynamicMemberNames()
{
return target.DynamicPropertyMemberNames;
}

public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
{
BindingRestrictions restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType);
System.Linq.Expressions.Expression self = System.Linq.Expressions.Expression.Convert(Expression, LimitType);

if (binder == null) return null;

var body = System.Linq.Expressions.Expression.Property(self, "Item", System.Linq.Expressions.Expression.Constant(binder.Name));
var convert = System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression.Constant(value.Value), typeof(object));
var lambda = System.Linq.Expressions.Expression.Assign(body, convert);

return new DynamicMetaObject(lambda, restrictions);
}

public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
{
BindingRestrictions restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType);
System.Linq.Expressions.Expression self = System.Linq.Expressions.Expression.Convert(Expression, LimitType);

if (binder == null) return null;

var body = System.Linq.Expressions.Expression.Property(self, "Item", System.Linq.Expressions.Expression.Constant(binder.Name));

return new DynamicMetaObject(body, restrictions);
}
}
}

这是怎么回事??

最佳答案

您对 DynamicModelObject 的实现不正确。在 DynamicDictionaryPropertyStore 子类中,您执行以下操作:

public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) {
BindingRestrictions restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType);
System.Linq.Expressions.Expression self = System.Linq.Expressions.Expression.Convert(Expression, LimitType);

if (binder == null) return null;
var body = System.Linq.Expressions.Expression.Property(self, "Item", System.Linq.Expressions.Expression.Constant(binder.Name));
var convert = System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression.Constant(value.Value), typeof(object));
var lambda = System.Linq.Expressions.Expression.Assign(body, convert);

return new DynamicMetaObject(lambda, restrictions);
}

如果您查看得到的表达式,您会看到(例如 PropA):

Convert($arg0).Item["PropA"] = Convert("1")

因此,作为 setter ,您返回调用索引器并分配常量值 (1) 的表达式,而不管实际传递的值是什么。此表达式稍后将用于 PropA(已缓存)的所有 setter 。因此你的问题:你所有的设置者都会忽略传递的值,并且总是会分配你第一次调用它们的值。要修复,请替换此行:

var convert = System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression.Constant(value.Value), typeof(object));

有了这个:

var convert = System.Linq.Expressions.Expression.Convert(value.Expression, typeof(object));

生成的 setter 表达式将是:

Convert($arg0).Item["PropA"] = Convert($arg1)

请注意,这里没有常数,只有参数。之后你的问题就解决了。

关于c# - IDynamicMetaObjectProvider 实现反序列化类的 Newtonsoft 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42557871/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com