2012-12-19 81 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仍然可以调用它,因为它通过反射来完成。

相关问题