这是这个问题的后续问题。我试图实现的是从我的类中替换[JsonConstructor]
,以便在我不进行任何序列化的项目中摆脱对 Json.Net 的依赖。根据我从其他来源收集到的内容,使用ContractResolver
是实现某种"对于Foo类型使用委托栏来创建它的实例"映射的方法(参见MongoDB的类映射)。对我来说重要的是,我想在我的实体中坚持使用我的只读字段,因此我必须使用一些带有参数的工厂方法来创建实例,同时在创建过程中将给定的参数分配给只读字段。
我提出了以下解决方案(我从一个公共无参数构造函数,到一个特性化构造函数,到一个私有无参数构造函数,再到根本没有属性化构造函数,并且每一步我都探索了合约解析器覆盖方法中的行为):
class Thing
{
public int Id { get; private set; }
public string Name { get; private set; }
//private Thing() { }
//[JsonConstructor]
private Thing(int id, string name)
{
this.Id = id;
this.Name = name;
}
public static Thing Create(int id, string name)
{
return new Thing(id, name);
}
public static object Create2(object[] values)
{
return new Thing((int)values[0], (string)values[1]);
}
}
我的合约解析器如下所示:
class CustomResolver : DefaultContractResolver
{
public override JsonContract ResolveContract(Type type)
{
var b = base.ResolveContract(type);
if(type == typeof(Thing))
{
var bb = b as JsonObjectContract;
// figured out the name of the backing field using ILSpy
var prop = typeof(JsonObjectContract).GetField("_creatorParameters",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
// in order to set the value of property CreatorParameters
// which is used as parameter when OverrideCreator is invoked
prop.SetValue(bb, bb.Properties);
bb.OverrideCreator = new ObjectConstructor<object>(Thing.Create2);
}
return b;
}
}
这是它的用法:
Thing t1 = Thing.Create("Flim", 1);
// Serializer settings
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new CustomResolver();
settings.PreserveReferencesHandling = PreserveReferencesHandling.None;
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
settings.Formatting = Newtonsoft.Json.Formatting.Indented;
string json = JsonConvert.SerializeObject(t1, settings);
Thing t2 = JsonConvert.DeserializeObject<Thing>(json, settings);
它按照我的预期工作,但由于我已经通过使用反射入侵了系统,我确信我正在做一些完全错误的事情,并且必须有一种更清洁的方法。有什么想法吗?
编辑
直到现在我还没有想到的是,我有一些类被赋予[JsonConverter(typeof(SomeSubstituteType))]
,如果我想完全消除这种缺陷,我也会摆脱这些类。到目前为止,这些类未包含在协定解析程序类的类型检查中。有没有一种优雅的方法可以让它在没有属性的情况下工作,并且无需在解析器中添加额外的检查,方法是以某种方式将JsonSerializerSettings
配置为到目前为止配置的内容?
确实,DefaultContractResolver
目前并不容易注入逻辑来选择自定义构造函数,因为:
-
JsonObjectContract.OverrideConstructor
被标记为已过时。 -
DefaultContractResolver.GetAttributeConstructor
是私有和非虚拟的。
不过,您可以对代码进行以下改进:
-
覆盖
CreateObjectContract
而不是ResolveContract
。 第一次遇到给定类型类型的对象时,将调用前者,以便为随后缓存的类型构造协定。 后者是为遇到的每个对象(包括基元)调用的。 因此,覆盖前者将更具性能。 此外,CreateObjectContract()
位于线程安全锁内,而ResolveContract()
不在线程安全锁中,可以同时从多个线程调用。 因此,您当前的代码可能不是线程安全的。 -
JsonObjectContract.CreatorParameters
是一个可变集合,因此您只需清除内容并添加新属性即可。
因此,您的合约解析器可能如下所示:
class CustomResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
if (objectType == typeof(Thing))
{
contract.CreatorParameters.Clear();
// For safety, specify the order concretely.
contract.CreatorParameters.AddProperty(contract.Properties["Id"]); // Use nameof() in latest version.
contract.CreatorParameters.AddProperty(contract.Properties["Name"]); // Use nameof() in latest version.
contract.OverrideCreator = new ObjectConstructor<object>(Thing.Create2);
}
return contract;
}
}
示例小提琴。
更新
我有一些归因于[JsonConverter(typeof(SomeSubstituteType))]
的类,如果我想完全消除缺点,我也会摆脱这些类......有没有一种优雅的方法可以让它在没有属性的情况下工作,并且无需在解析器中添加额外的检查,方法是以某种方式将JsonSerializerSettings
配置为目前配置的内容?
是的,请确保为您的转换器实现了JsonConverter.CanConvert()
,然后将其添加到JsonSerializerSettings.Converters
:
public class SomeSubstituteType : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(SomeOriginalType).IsAssignableFrom(objectType);
}
// Remainder as before
}
然后做:
var settings = new JsonSerializerSettings
{
Converters = { new SomeSubstituteType(), new SecondConverter(), ... },
};