如何在不调用构造函数的情况下反序列化类?

时间:2012-12-19 08:10:37

标签: c# json.net deserialization

我在WCF数据服务中使用Json.NET。

这是我的课程(简化):

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

如何在不使用使用JsonConvert.DeserializeObject调用构造函数的情况下反序列化该类

对不起,如果不清楚,请随时提问。

4 个答案:

答案 0 :(得分:11)

始终调用构造函数。我通常有两个构造函数。一个用于序列化(默认构造函数),一个用于所有“常规”代码:

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

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

通过这种方式,我还可以确保开发人员指定所需的所有信息。

但是,我并不建议您在传输信息时使用除DTO之外的任何东西,因为否则可能会绕过对象的封装(任何人都可以使用任何值初始化任何字段)。好。如果您使用的只是贫血模型。

使用FormatterServices.GetSafeUninitializedObject因此是一个丑陋的解决方法,因为没有人可以告诉你以非实体方式创建所有对象。构造函数初始化是有原因的。最好的是,类可以通过提供我建议的“序列化”构造函数来告诉可以不调用真正的构造函数。

答案 1 :(得分:7)

  1. 您可以创建一个继承自CustomCreationConverter的类 并使用FormatterServices.GetSafeUninitializedObject创建您的 宾语。它会跳过调用构造函数。

    有关CustomCreationConverter here的更多信息。

  2. 配售 类上的[JsonObject(MemberSerialization.Fields)]将使Json.NET使用 默认为FormatterServices.GetSafeUninitializedObject(尽管如此 字段模式也将序列化公共/私有字段而不是 您可能不想要的公共财产。

  3. 移动您不想在默认构造函数之外运行的逻辑。

答案 2 :(得分:2)

其他人已经提到了第二个构造函数,但使用了2个属性:[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仍然可以调用它,因为它通过反射来实现它。

答案 3 :(得分: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;
    }
}

您只需在反序列化之前在序列化设置中设置此合约解析器。