2017-03-02 31 views
2

当前我偶然发现了Newtonsofts Json库的一个问题,这对我来说完全是个谜。IDynamicMetaObjectProvider在反序列化中实现类的Newtonsoft问题

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

然而反序列化让我头痛。从我目前为止所观察到的情况来看,似乎该库为每个无法找到的动态属性缓存值,并在应用程序运行时保留该值。因此,作为例子,我有以下三个Jsons:

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

连续Deserialising这个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); 
      } 
     } 
    } 

怎么回事?

+0

你可以提供实现IDynamicMetaObjectProvider的类吗? – Evk

回答

2

您的实施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") 

因此,作为一个二传手返回表达式,它会调用您的索引器并分配常量值(1),而不管实际传递的值是多少。这个表达式将在稍后用于所有设置器到PropA(缓存)。因此,你的问题:所有的设置者将忽略传递的值,并且会始终分配第一次调用它们的值。要修复,替换此行:

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)); 

得到的制定者表达是:

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

注意,没有costants在那里,只是争论。之后,你的问题将得到解决。

+0

谢谢!它正在按预期工作。我从来没有在这个地方搜过,但看到你的答案是完全可以理解的,为什么会发生这种情况。 –