作为这个问题的一个部分问题,带有嵌套对象的JSON.NET CustomCreationConverter我试图在反序列化过程中调用自定义构造函数。我简化的类层次结构如下:
public abstract class BusinessObjectBase
{
internal BusinessObjectBase(SerializationContext context)
: base(context)
{
}
}
public abstract class EditableObjectBase : BusinessObjectBase
{
protected EditableObjectBase(SerializationContext context)
: base(context)
{
}
}
public class EditableObjectCollection<TObject> : BusinessObjectBase, ICollection<TObject>, IList, INotifyCollectionChanged where TObject : BusinessObjectBase
{
protected EditableObjectCollection(SerializationContext context)
: base(context)
{
}
}
我对对象层次结构有一定的了解,但用户可以/被迫派生自己的类。我的想法是编写一个自定义创建转换器。我要解决的问题是,序列化对象中的属性可以声明为抽象的BusinessObjectBase,但实际对象将是一个更派生的类,可能是集合,也可能不是集合。CustomCreationConverter只将此抽象类型传递给Create方法,当然无法根据此信息创建正确的类型。
受此启发如何在JSON.NET中实现自定义JsonConverter以反序列化基类对象列表我实现了一个转换器,如下所示:
internal class BusinessObjectCreationConverter : JsonConverter
{
public override bool CanWrite
{
get
{
return false;
}
}
public override bool CanConvert(Type objectType)
{
return typeof(BusinessObjectBase).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object result = null;
if (reader.TokenType != JsonToken.Null)
{
JObject jsonObject = JObject.Load(reader);
result = this.Create(objectType, jsonObject);
Verification.Assert<NullReferenceException>(result != null, "No Business Object created.");
serializer.Populate(jsonObject.CreateReader(), result);
}
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
}
public BusinessObjectBase Create(Type objectType, JObject jsonObject)
{
JToken token = jsonObject.SelectToken("$type");
var typeString = token.Value<string>();
Type type = Type.GetType(typeString);
var businessObject = type.CreateUsingDesrializationConstructor<BusinessObjectBase>();
businessObject.Initialize(true);
return businessObject;
}
}
我要测试序列化的类如下:
public class AnyPocoContainingBusinessObject
{
public BusinessObjectBase BusinessObject { get; set; }
}
public class TestEditableObject : EditableObjectBase
{
internal TestEditableObject(SerializationContext context)
: base(context)
{
}
}
如果我用集合初始化我的类
var collection = new EditableObjectCollection<TestEditableObject>(null);
var poco = new AnyPocoContainingBusinessObject { BusinessObject = collection };
并以这种方式配置串行器:
public NewtonsoftJsonSerializer()
: this(new JsonSerializer
{
TypeNameHandling = TypeNameHandling.Auto,
ObjectCreationHandling = ObjectCreationHandling.Replace,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
DefaultValueHandling = DefaultValueHandling.Ignore,
ContractResolver = new KsJsonContractResolver()
})
{
this.serializer.Converters.Add(new ReadOnlyObjectCollectionConverter());
this.serializer.Converters.Add(new BusinessObjectCreationConverter());
this.serializer.TraceWriter = new ConsoleTraceWriter();
}
我得到一个例外:无法将JSON对象填充到类型"KS.Interfaces.Core.Entities.EditableObjectCollection`1[KS.Interfaces.Core.Editives.Tests.Unit.EditableObject CollectionTests+TestEditableObject]"中。路径"$type",第1行,位置47。
在我的转换器的代码行中:
serializer.Populate(jsonObject.CreateReader(), result);
有人能告诉我可能是什么原因吗?我确信我创建了正确的类型,并且使用EditableObjectBase派生的对象,一切都很好。只有收藏似乎不起作用。
非常感谢任何提示,提前感谢Carsten
即使我还没有找到让转换器工作的方法,但在调试问题时我学到了一件事:
转换器似乎应该返回一个仍然具有相同JsonToken值的对象。在我的例子中,原始对象的JsonToken是JsonToken.object,但对于我的转换结果,正确的令牌值应该是JsonToken。Array,但读者仍然可以看到JsonToken。object。在这一点上,我停止了研究,因为我找到了一种更好的方法来调用我的自定义构造函数。
我写了自己的合同解析程序:
internal class BusinessBaseContractResolver : DefaultContractResolver
{
public BusinessBaseContractResolver()
{
this.DefaultMembersSearchFlags |= BindingFlags.NonPublic;
}
public override JsonContract ResolveContract(Type type)
{
JsonContract contract = base.ResolveContract(type);
if (typeof(BusinessObjectBase).IsAssignableFrom(type))
{
contract.DefaultCreator = delegate
{
var businessObject = type.CreateUsingDeserializationConstructor<BusinessObjectBase>();
businessObject.Initialize(true);
return businessObject;
};
}
return contract;
}
}
我希望这能帮助到别人。
谨致问候,Carsten