我在我的 WCF 数据服务中使用 Json.NET。
这是我的课(简化版):
[DataContract]
public class Component
{
public Component()
{
// I'm doing some magic here.
}
}
如何在不使用构造函数的情况下反序列化该类JsonConvert.DeserializeObject
?
抱歉,如果不清楚,请随时提出问题。
我在我的 WCF 数据服务中使用 Json.NET。
这是我的课(简化版):
[DataContract]
public class Component
{
public Component()
{
// I'm doing some magic here.
}
}
如何在不使用构造函数的情况下反序列化该类JsonConvert.DeserializeObject
?
抱歉,如果不清楚,请随时提出问题。
构造函数总是被调用。我通常有两个构造函数。一个用于序列化(默认构造函数),一个用于所有“常规”代码:
[DataContract]
public class Component
{
// for JSON.NET
protected Component()
{
}
public Component(allMandatoryFieldsHere)
{
// I'm doing some magic here.
}
}
这样,我还可以确保开发人员指定了所有需要的信息。
但是,我真的不建议您在传输信息时使用 DTO 以外的任何东西,因为否则可能会绕过对象的封装(任何人都可以使用任何值初始化任何字段)。好。如果您使用除贫血模型之外的任何东西。
因此,使用FormatterServices.GetSafeUninitializedObject
是一种丑陋的解决方法,因为没有人可以告诉您以未初始化的方式创建所有对象。构造函数初始化是有原因的。最好是类可以通过提供我建议的“序列化”构造函数来告诉不调用真正的构造函数是可以的。
您可以创建一个继承自CustomCreationConverter
并用于FormatterServices.GetSafeUninitializedObject
创建对象的类。它跳过调用构造函数。
更多关于 CustomCreationConverter的信息。
放置
[JsonObject(MemberSerialization.Fields)]
在一个类上将使 Json.NET
FormatterServices.GetSafeUninitializedObject
默认使用(尽管字段模式还将序列化公共/私有字段而不是您可能不想要的公共属性)。
将您不想在默认构造函数之外运行的逻辑移动。
其他人已经提到了第二个构造函数,但是使用 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 调用它而不是默认值。
[Obsolete("...", true)] 确保直接从代码中调用它会产生编译器错误。JSONserializer 仍然可以调用它,因为它是通过反射来实现的。
避免在反序列化时调用构造函数的最佳选择是创建特殊的合约解析器,该解析器覆盖所有类的创建者函数,而构造函数没有用 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;
}
}
您只需在反序列化之前在序列化程序设置中设置此合约解析器即可。