2016-09-06 62 views
1

考慮下面的序列化的類結構:JSON.NET甚至調用接口屬性getter時`ObjectCreationHandling`設置爲`Replace`

public class Foo 
{ 
    private IBar bar; 
    public IBar Bar 
    { 
     get 
     { 
      if(this.bar == null) 
       throw new InvalidOperationException("bar must be set before it is read"); 
      return this.bar; 
     } 
     set 
     { 
      if(value == null) 
       throw new ArgumentNullException("value"); 
      this.bar = value; 
     } 
    } 
} 
public interface IBar { } 
public class Bar : IBar { } 

而且下面的自定義JsonConverter確保IBar被轉換爲Bar

:因爲JSON.NET試圖調用時該屬性從未設置 Foo.Bar吸氣
public class BarConverter : JsonConverter 
{ 
    public override bool CanWrite => false; 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(IBar); 
    } 
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     var jobject = JObject.Load(reader); 
     return jobject.ToObject(typeof(Bar), serializer); 
    } 
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotSupportedException("This should not be invoked."); 
    } 
} 

下面的代碼失敗

var settings = new JsonSerializerSettings 
{ 
    ObjectCreationHandling = ObjectCreationHandling.Replace, 
    Converters = { new BarConverter() } 
}; 
JsonConvert.DeserializeObject<Foo>("{Bar: {}}", settings); 

我的理解是ObjectCreationHandling.Replace應該防止調用getter。事實上,如果我將Bar屬性的類型更改爲具體的Bar類而不是IBar接口,那麼ObjectCreationHandling.Replace工作得很好。

這是JSON.NET中的缺陷,還是有一個原因,聲明爲接口的屬性應忽略ObjectCreationHandling值?

回答

1

這不是事實,該屬性被聲明爲接口,ObjectCreationHandling設置被忽略;這是因爲轉換器正在爲該屬性發揮作用而被忽略。當您將屬性聲明從接口IBar更改爲具體的Bar時,轉換器中的CanConvert()方法返回false,因爲objectType不等於IBar。這會將您的轉換器從等式中移除,因此Json.Net採用考慮ObjectCreationHandling設置並且不調用getter的「正常」代碼路徑。如果將CanConvert()的實現更改爲 return typeof(IBar).IsAssignableFrom(objectType); 那麼您將看到無論屬性是聲明爲IBar還是Bar,都會調用getter。

我相信這種行爲是有目的的,而不是缺陷。 ReadJson方法的合同要求傳遞對象的現有值(通過參數existingValue),以便轉換器可以決定如何處理該對象(如果有)。沒有辦法讓Json.Net在不調用getter的情況下確定現有值是什麼,因此必須調用它。

如果我是你,我會更改Bar getter實現,如果可以的話不會拋出異常。每Microsoft recommendations它被認爲是不好的做法,一個屬性的getter內拋出異常:

AVOID投擲物業干將例外。

屬性獲取者應該是簡單的操作,不應該有任何先決條件。如果一個getter可以拋出一個異常,它應該重新設計成一個方法。請注意,此規則不適用於索引器,我們確實希望通過驗證參數來預期異常。

+0

優秀的偵查。謝謝。 – StriplingWarrior

+0

很高興我能幫到你。 –

相關問題