2012-12-19 130 views
16

我在我的WCF數據服務使用Json.NET。如何在不調用構造函數的情況下反序列化類?

這裏是我的課(簡體):

[DataContract] 
public class Component 
{ 
    public Component() 
    { 
     // I'm doing some magic here. 
    } 
} 

我如何反序列化類調用使用JsonConvert.DeserializeObject構造?

對不起,如果不清楚,隨時提問。

+0

不可能AFAIK。創建實例時始終執行構造函數。 – Maarten

+3

@Maarten http://msdn.microsoft.com/zh-cn/library/system.runtime.serialization.formatterservices.getsafeuninitializedobject.aspx –

+0

Thanx!我今天學到了一些東西:-) – Maarten

回答

7
  1. 您可以創建一個從CustomCreationConverter 繼承的類,並使用FormatterServices.GetSafeUninitializedObject創建您 對象。它跳過調用構造函數。

    更多CustomCreationConverter here

  2. 一類配售 [JsonObject(MemberSerialization.Fields)]會讓Json.NET使用 FormatterServices.GetSafeUninitializedObject默認情況下(雖然 場模式也將序列化的公共/私有字段,而不是 公共屬性,您可能不希望)。

  3. 打動你不想默認構造函數之外運行的邏輯。

+0

非常感謝!出於某種原因,當我嘗試'[JsonObject(MemberSerialization.Fields)]'時,它產生了'StackOverflowException'。所以我更喜歡第一個變體。你能告訴我 - 有沒有辦法爲許多類創建通用的'CustomCreationConverter'? –

+1

複製CustomCreationConverter源代碼並修改它以做你想做的事。它只是繼承自JsonConverter - https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Converters/CustomCreationConverter.cs –

10

構造函數總是被調用。我通常有兩個構造函數。一個序列化(默認構造函數),一個用於所有的「正規」的代碼:

[DataContract] 
public class Component 
{ 
    // for JSON.NET 
    protected Component() 
    { 
    } 

    public Component(allMandatoryFieldsHere) 
    { 
     // I'm doing some magic here. 
    } 
} 

這樣一來我也可以確保在開發指定其所需的所有信息。

但是,我不建議您在傳輸信息時使用除DTO以外的任何內容,否則可能會繞過對象封裝(任何人都可以使用任何值初始化任何字段)。好。如果你使用任何東西,但貧血模型。

使用FormatterServices.GetSafeUninitializedObject是恕我直言,因此一個醜陋的解決辦法,因爲沒有人能告訴你在unintialized方式創建的所有對象。構造函數初始化是有原因的。最好是類可以通過提供一個「序列化」構造函數來調用真正的構造函數,正如我所建議的那樣。

+0

但據我所知,默認'DataContractJsonSerializer'不會調用構造函數。 –

+0

@IgorShastin:這就是爲什麼你只能使用它與DTO的。 JSON.NET的表現更好。 – jgauffin

0

,以避免反序列化構造函數調用最好的辦法是建立專項合同解析器,它覆蓋的創造者功能沒有打上JsonConstructor屬性構造的所有類。這樣,如果你真的需要它,你仍然可以強制JSON.NET調用構造函數,但是所有其他的類將被創建得非常像.NET中的標準DataContract序列化器。這裏是代碼:

/// <summary> 
/// Special contract resolver to create objects bypassing constructor call. 
/// </summary> 
public class NoConstructorCreationContractResolver : DefaultContractResolver 
{ 
    /// <summary> 
    /// Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract"/> for the given type. 
    /// </summary> 
    /// <param name="objectType">Type of the object.</param> 
    /// <returns> 
    /// A <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract"/> for the given type. 
    /// </returns> 
    protected override JsonObjectContract CreateObjectContract(Type objectType) 
    { 
     // prepare contract using default resolver 
     var objectContract = base.CreateObjectContract(objectType); 

     // if type has constructor marked with JsonConstructor attribute or can't be instantiated, return default contract 
     if (objectContract.OverrideConstructor != null || objectContract.CreatedType.IsInterface || objectContract.CreatedType.IsAbstract) 
      return objectContract; 

     // prepare function to check that specified constructor parameter corresponds to non writable property on a type 
     Func<JsonProperty, bool> isParameterForNonWritableProperty = 
      parameter => 
      { 
       var propertyForParameter = objectContract.Properties.FirstOrDefault(property => property.PropertyName == parameter.PropertyName); 

       if (propertyForParameter == null) 
        return false; 

       return !propertyForParameter.Writable; 
      };     

     // if type has parameterized constructor and any of constructor parameters corresponds to non writable property, return default contract 
     // this is needed to handle special cases for types that can be initialized only via constructor, i.e. Tuple<> 
     if (objectContract.ParametrizedConstructor != null 
      && objectContract.ConstructorParameters.Any(parameter => isParameterForNonWritableProperty(parameter))) 
      return objectContract; 

     // override default creation method to create object without constructor call 
     objectContract.DefaultCreatorNonPublic = false; 
     objectContract.DefaultCreator =() => FormatterServices.GetSafeUninitializedObject(objectContract.CreatedType); 

     return objectContract; 
    } 
} 

所有你需要的只是在反序列化之前在串行器設置中設置這個合約解析器。

1

其他人已經提到了第二個構造函數,但使用了兩個屬性:[JsonConstructor]和[Obsolete],你可以做得比把它留給人類記住哪一個更好。

public ChatMessage() 
    { 
     MessageID = ApplicationState.GetNextChatMessageID(); // An expensive call that uses up an otherwise free ID from a limited set and does disk access in the process. 
    } 


    [JsonConstructor] // This forces JsonSerializer to call it instead of the default. 
    [Obsolete("Call the default constructor. This is only for JSONserializer", true)] // To make sure that calling this from your code directly will generate a compiler error. JSONserializer can still call it because it does it via reflection. 
    public ChatMessage(bool DO_NOT_CALL_THIS) 
    { 
    } 

[JsonConstructor]力JsonSerializer調用它,而不是默認的。
[已廢棄(「...」,true)]確保直接從您​​的代碼中調用此代碼會生成編譯器錯誤。 JSONserializer仍然可以調用它,因爲它通過反射來完成。

相關問題