19

WCF データ サービスで Json.NET を使用しています。

これが私のクラスです(簡略化):

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

を使用してコンストラクターを呼び出さに、そのクラスを逆シリアル化するにはどうすればよいJsonConvert.DeserializeObjectですか?

わかりずらくてすみません、お気軽に質問してください。

4

4 に答える 4

11

コンストラクターは常に呼び出されます。私は通常2つのコンストラクターを持っています。1 つはシリアル化 (デフォルトのコンストラクター) 用で、もう 1 つはすべての「通常の」コード用です。

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

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

そのようにして、開発者が必要なすべての情報を指定していることを確認することもできます。

ただし、情報を転送するときに DTO 以外を使用することはお勧めしません。そうしないと、オブジェクトのカプセル化を回避できるからです (誰でも任意のフィールドを任意の値で初期化できます)。良い。貧血モデル以外を使用する場合。

したがって、すべてのオブジェクトを初期化されていない方法で作成しているとは誰にもわからないため、使用FormatterServices.GetSafeUninitializedObjectは見苦しい回避策です。コンストラクターの初期化には理由があります。私が提案したように、「シリアル化」コンストラクターを提供することにより、クラスが実際のコンストラクターを呼び出さなくてもよいことを伝えることができる方が良いです。

于 2012-12-19T08:14:18.120 に答える
7
  1. オブジェクトを作成するために継承しCustomCreationConverter て使用するクラスをFormatterServices.GetSafeUninitializedObject作成できます。コンストラクターの呼び出しをスキップします。

    CustomCreationConverter の詳細については、こちらをご覧ください。

  2. クラスに配置 [JsonObject(MemberSerialization.Fields)]すると、デフォルトで Json.NET が使用 FormatterServices.GetSafeUninitializedObjectされます (ただし、フィールド モードでは、不要なパブリック プロパティではなく、パブリック/プライベート フィールドもシリアル化されます)。

  3. 実行したくないロジックをデフォルトのコンストラクタの外に移動します。

于 2012-12-19T09:51:11.067 に答える
2

他の人はすでに 2 番目のコンストラクターについて言及していますが、[JsonConstructor] と [Obsolete] の 2 つの属性を使用すると、どちらを呼び出すかを人間に任せるよりもはるかに優れたことができます。

    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 を呼び出します。
[Obsolete("...", true)] これをコードから直接呼び出すとコンパイラ エラーが生成されるようにします。JSONserializer は、リフレクションを介して呼び出すため、引き続き呼び出すことができます。

于 2016-08-25T19:34:09.660 に答える
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;
    }
}

必要なのは、逆シリアル化の前にシリアライザー設定でこのコントラクト リゾルバーを設定するだけです。

于 2014-01-22T09:15:58.260 に答える