C# 如何在不调用构造函数的情况下反序列化类?
我在WCF数据服务中使用Json.NET 这是我的课程(简体): 如何在不使用C# 如何在不调用构造函数的情况下反序列化类?,c#,json.net,deserialization,C#,Json.net,Deserialization,我在WCF数据服务中使用Json.NET 这是我的课程(简体): 如何在不使用JsonConvert.DeserializeObject调用构造函数的情况下反序列化该类 抱歉,如果不清楚,请随时提问。始终会调用构造函数。我通常有两个构造函数。一个用于序列化(默认构造函数),一个用于所有“常规”代码: 通过这种方式,我还可以确保开发人员指定所需的所有信息 但是,我并不建议您在传输信息时使用DTO以外的任何东西,因为它可以绕过对象的封装(任何人都可以用任何值初始化任何字段)。好。如果你不使用贫血模型
JsonConvert.DeserializeObject
调用构造函数的情况下反序列化该类
抱歉,如果不清楚,请随时提问。始终会调用构造函数。我通常有两个构造函数。一个用于序列化(默认构造函数),一个用于所有“常规”代码: 通过这种方式,我还可以确保开发人员指定所需的所有信息 但是,我并不建议您在传输信息时使用DTO以外的任何东西,因为它可以绕过对象的封装(任何人都可以用任何值初始化任何字段)。好。如果你不使用贫血模型 因此,使用
FormatterServices.GetSafeUninitializedObject
是一种难看的解决方法,因为没有人知道您是以未初始化的方式创建所有对象的。构造函数初始化是有原因的。更好的是,这些类可以通过提供我建议的“序列化”构造函数来判断不调用真正的构造函数是可以的
CustomCreationConverter
并使用FormatterServices.GetSafeUninitializedObject
创建
对象它跳过调用构造函数
有关CustomCreationConverter的更多信息FormatterServices.GetSafeUninitializedObject
(尽管
字段模式还将序列化公共/私有字段,而不是
您可能不需要的公共属性)避免在反序列化时调用构造函数的最佳选择是创建特殊的协定解析程序,该解析程序覆盖所有未使用JsonConstructor属性标记的构造函数的类的creator函数。这样,如果确实需要,您仍然可以强制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;
}
}
//
///用于绕过构造函数调用创建对象的特殊协定解析器。
///
公共类NoConstructor CreationContractResolver:DefaultContractResolver
{
///
///为给定类型创建一个。
///
///对象的类型。
///
///对于给定类型的。
///
受保护的重写JsonObjectContract CreateObjectContract(类型objectType)
{
//使用默认解析器准备合同
var objectContract=base.CreateObjectContract(objectType);
//若类型的构造函数标记有JsonConstructor属性或无法实例化,则返回默认约定
if(objectContract.OverrideConstructor!=null | | | objectContract.CreatedType.IsInterface | | | objectContract.CreatedType.IsAbstract)
退货合同;
//准备函数以检查指定的构造函数参数是否对应于类型的不可写属性
Func isParameterForNonWritableProperty=
参数=>
{
var propertyForParameter=objectContract.Properties.FirstOrDefault(property=>property.PropertyName==parameter.PropertyName);
if(propertyForParameter==null)
返回false;
return!propertyForParameter.Writable;
};
//如果类型具有参数化构造函数,并且任何构造函数参数对应于不可写属性,则返回默认约定
//这是处理只能通过构造函数初始化的类型(即元组)的特殊情况所必需的
如果(objectContract.ParameterizedConstructor!=null
&&objectContract.ConstructorParameters.Any(参数=>isParameterForNonWritableProperty(参数)))
退货合同;
//重写默认创建方法以创建对象而不调用构造函数
objectContract.DefaultCreatorNonPublic=false;
objectContract.DefaultCreator=()=>FormatterServices.GetSafeUninitializedObject(objectContract.CreatedType);
退货合同;
}
}
您只需在反序列化之前在序列化程序设置中设置此协定解析程序即可。其他人已经提到了第二个构造函数,但使用两个属性:[JsonConstructor]和[Observe],您可以做得比让人记住调用哪一个要好得多
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仍然可以调用它,因为它是通过反射完成的。不可能。创建实例时总是执行构造函数。@Maarten Thanx!今天我学到了一些东西:-)你可以用你的逻辑添加一个
Initialize
方法。不幸的是,这需要消耗性的代码更改。@jgauffin:你说得对。您的解决方案更好,但是如果没有要传递给构造函数的参数呢?但是据我所知,默认的DataContractJsonSerializer
不会调用构造函数。@IgorShastin:这就是为什么您只应该将它与DTO一起使用。JSON.NET表现得更好。非常感谢!出于某种原因,当我尝试[JsonObject(MemberSerialization.Fields)]
时,它产生了StackOverflowException
。所以我更喜欢第一种变体。您能告诉我-有没有一种方法可以为许多类创建universalCustomCreationConverter
?复制CustomCreationConverter源代码,并对其进行修改以满足您的需要。它只是从JsonConverter继承的-
/// <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;
}
}
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)
{
}