2012-12-19 5 views
16

WCF 데이터 서비스에서 Json.NET을 사용하고 있습니다.생성자를 호출하지 않고 클래스를 deserialize하는 방법은 무엇입니까?

여기 (간체) 내 클래스의 :

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

가 어떻게이 JsonConvert.DeserializeObject를 사용하여 생성자를 호출하지 않고 클래스 를 역 직렬화 할 수 있습니까?

죄송합니다. 명확하지 않은 경우 언제든지 질문하십시오.

+0

AFAIK. 생성자는 인스턴스를 만들 때 항상 실행됩니다. – Maarten

+3

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

+0

고맙습니다. 오늘 뭔가를 배웠습니다 :-) – Maarten

답변

7
  1. 당신은 CustomCreationConverter 에서 상속하는 클래스를 만들고 개체를 만들 수 FormatterServices.GetSafeUninitializedObject를 사용할 수 있습니다. 생성자 호출을 건너 뜁니다.

    CustomCreationConverter에 대한 추가 정보 here. ( 필드 모드는 당신이 원하는하지 않을 수 있습니다 공공/민간 분야보다는 공용 속성을 직렬화지라도) Json.NET 기본적으로 FormatterServices.GetSafeUninitializedObject을 사용하게됩니다 클래스에 [JsonObject(MemberSerialization.Fields)] 배치

  2. .

  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

항상 생성자가 호출됩니다. 보통 두 개의 생성자가 있습니다. 직렬화에 대한 하나의 (기본 생성자) 및 모든 "일반"코드 하나 : 나는 또한 dev에 필요한 모든 정보를 지정해야합니다 수있는 방법으로

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

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

.

그러나 정보를 전송할 때 DTO를 사용하는 것이 좋습니다. 그렇지 않으면 개체의 캡슐화를 피할 수 있기 때문에 (누군가가 모든 값으로 필드를 초기화 할 수 있기 때문에) 정보를 전송할 때 DTO를 사용하는 것이 좋습니다. 잘. 빈혈 이외에 아무것도 사용하지 않는 모델.

사용 따라서 은 모든 개체를 비 고유화 된 방식으로 만들었다는 것을 아무도 알 수 없으므로 추악한 해결 방법입니다. 생성자 초기화가 이유가 있습니다. 제가 제안한 것처럼 "직렬화"생성자를 제공함으로써 실제 생성자를 호출하지 않는 것이 클래스에서 알 수 있습니다.

+0

그러나 내가 아는 한 기본'DataContractJsonSerializer'는 생성자를 호출하지 않습니다. –

+0

@IgorShastin : 그래서 DTO에서만 사용해야합니다. JSON.NET이 더 잘 작동합니다. – jgauffin

0

직렬화에 생성자 호출을 피할 수있는 최선의 선택은 JsonConstructor 속성으로 표시 생성자없이 모든 클래스에 대한 생성자 함수를 오버라이드 (override) 특별 계약 리졸버를 만드는 것입니다. 이 방법으로 JSON.NET이 정말로 필요한 경우 생성자를 호출하도록 강요 할 수 있지만 다른 모든 클래스는 .NET의 표준 DataContract serializer와 매우 비슷하게 생성됩니다. 코드는 다음과 같습니다.

/// <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는 리플렉션을 통해이 작업을 수행하므로 여전히 호출 할 수 있습니다.

관련 문제