这是我的类(简化版):
[DataContract]
public class Component
{
public Component()
{
// I'm doing some magic here.
}
}
我如何在使用JsonConvert.DeserializeObject
时,不调用构造函数进行反序列化?
如果不清楚,请随时问问题。
[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
是一种丑陋的解决方法,不会有人知道您以未初始化的方式创建了所有对象。构造函数初始化是有原因的。最好通过提供“序列化”构造函数让类知道无需调用真正的构造函数就可以正常工作,就像我建议的那样。
DataContractJsonSerializer
默认情况下不会调用构造函数。 - Igor Shastin你可以创建一个继承自CustomCreationConverter
的类,并使用FormatterServices.GetSafeUninitializedObject
创建对象,这样可以跳过调用构造函数。
关于CustomCreationConverter的更多信息请点击这里。
在一个类上添加[JsonObject(MemberSerialization.Fields)]
将使Json.NET默认使用FormatterServices.GetSafeUninitializedObject
(虽然Fields模式也会序列化公共/私有字段而不是公共属性,这可能不是你想要的)。
将不想运行的逻辑移出默认构造函数。
[JsonObject(MemberSerialization.Fields)]
时,会出现StackOverflowException
异常。所以我更喜欢第一种方式。请问是否有办法为多个类创建通用的CustomCreationConverter
? - Igor Shastin其他人已经提到了第二个构造函数,但是使用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 仍然可以通过反射来调用它。
/// <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.OverrideCreator != 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.CreatorParameters.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;
}
}
您只需要在反序列化之前将此合同解析器设置在序列化程序设置中即可。
关于只读字段和只读属性的更新
如果一个只读字段有一个JsonProperty属性,则它可以被反序列化。 如果一个只读属性的后台字段有一个JsonProperty属性,则它也可以被反序列化。 自C# 7.3以来,可以将属性应用于自动属性的编译器生成的后台字段。
利用这一点,我们可以这样做:
public class SampleClass
{
[JsonProperty("Field1")]
private readonly string _field1 = "Field1Value";
[JsonProperty("Property1")]
private string _property1BackingField = "Property1Value";
[JsonIgnore]
public string Property1 => _property1BackingField;
[field: JsonProperty("Property2")]
[JsonIgnore]
public string Property2 { get; } = "Property2Value";
}
const string json = "{\"Field1\":\"NEW-Field1Value\",\"Property1\":\"NEW-Property1Value\",\"Property2\":\"NEW-Property2Value\"}";
var serializerSettings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
// this settings is needed to work with compiler-generated backing fields
SerializeCompilerGeneratedMembers = true
}
};
var deserializedSample = JsonConvert.DeserializeObject<SampleClass>(json, serializerSettings);
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var jsonProperty = base.CreateProperty(member, memberSerialization);
if (jsonProperty.Writable == false)
{
// this uses compiler implementation details and may not work for all cases
// better to use smarter approach like in BackingFieldResolver from Mono.Reflection library
var fieldInfo = jsonProperty.DeclaringType.GetField($"<{jsonProperty.PropertyName}>k__BackingField",
BindingFlags.NonPublic | BindingFlags.Instance);
if (fieldInfo != null)
{
jsonProperty.ValueProvider = new ReflectionValueProvider(fieldInfo);
jsonProperty.Writable = true;
}
}
return jsonProperty;
}