我仔细研究了这个问题,最接近我的情况并未解决我的担忧.
我有以下课程:
public abstract class BaseClass
{
}
public class ConcreteClass
{
}
Run Code Online (Sandbox Code Playgroud)
序列化和反序列化的设置对象如下:
JsonSerializerSettings _serializationSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
TypeNameHandling = TypeNameHandling.All,
ContractResolver = new CloudantContractResolver(),
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
Run Code Online (Sandbox Code Playgroud)
我试图像这样反序列化:
var myDeserializedObject = JsonConvert.DeserializeObject<BaseClass>(jsonString, _serializationSettings);
Run Code Online (Sandbox Code Playgroud)
但由于某种原因,我收到了错误
无法创建BaseClass类型的实例.Type是接口或抽象类,无法实例化.
即使根Json对象确实有$type
属性.我已经尝试反序列化到a JObject
然后使用JObject.To<BaseType>()
,但我有相同的结果.我需要使用这种方法,并且不希望使用自定义转换器,因为我在所有地方使用多态.
您对我如何使这种反序列化工作有任何想法吗?
更新10/10/15
我还在调查,我认为问题可能是当我检查JObject
我的反序列化对象时,第一个属性是_id
属性:
我假设,因为错误消息是:
JSON.NET可能需要首先读取类型以实例化正确的对象.我没有看到如何_id
从下面提供的单独项目中首先重现这种情况.我尝试了几种嵌套复杂属性的组合,但我总是有第$type
一个.这可能就是为什么它在那里工作得很好.
我试图CreateProperties
在ContractResolver上组合覆盖:
protected override System.Collections.Generic.IList<JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
var propWithDollar = properties.Where(x => x.PropertyName.Contains("$"));
foreach (var prop in propWithDollar)
{
properties.Remove(prop);
properties.Insert(0, prop);
}
return properties;
}
Run Code Online (Sandbox Code Playgroud)
但到目前为止,它对我的财产的顺序没有影响JObject
.
更新2
好的,所以我设法$type
通过使用以下方式将属性放在最顶层:
var prop = deserializedJObject.Property("$type");
deserializedJObject.Remove("$type");
deserializedJObject.AddFirst(prop);
Run Code Online (Sandbox Code Playgroud)
但不幸的是它没有帮助,我仍然面临同样的演员问题.
更新3
我已经能够重现这个问题.如果该$type
属性不是JSON字符串中的第一个属性,则会发生此错误.这显然是一个错误,因为JSON规范表明属性是无序的.
在我的情况下,我不太了解它,因为JSON对象是由总是放在_id
顶部的数据库返回的.我将在GitHub上记录一个问题,看看我是否可以提出解决方法.
这是一个重现问题的项目:http://we.tl/RiemGkRTF2
这个问题在Json.Net 6.0.3中得到了解决.来自作者的博客:
元数据属性处理
一些Json.NET序列化程序的功能,如保留类型或引用,需要Json.NET读取和写入元数据属性,例如$ type,$ id和$ ref.由于Json.NET反序列化的工作方式,这些元数据属性必须首先在JSON对象中进行排序.这可能会导致问题,因为无法在JavaScript和其他一些JSON框架中对JSON对象属性进行排序.
此版本添加了一个新设置,以允许元数据属性位于对象中的任何位置:
MetadataPropertyHandling.ReadAhead
Run Code Online (Sandbox Code Playgroud)string json = @"{ 'Name': 'James', 'Password': 'Password1', '$type': 'MyNamespace.User, MyAssembly' }"; object o = JsonConvert.DeserializeObject(json, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, // $type no longer needs to be first MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead }); User u = (User)o; Console.WriteLine(u.Name); // James
在内部,此设置将指示序列化程序将整个JSON对象加载到内存中.然后将从对象中读取元数据属性,然后反复序列化将继续正常进行.内存使用和速度有一点点成本,但如果您需要使用元数据属性的功能并且无法保证JSON对象属性顺序,那么您会发现这很有用.
最重要的是,添加MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead
到您的设置,这应该可以解决您的问题.