我查看了有关此主题的问题,the closest to my situation没有解决我的问题。
我有以下课程:
public abstract class BaseClass
{
}
public class ConcreteClass
{
}
序列化和反序列化的设置对象如下:
JsonSerializerSettings _serializationSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
TypeNameHandling = TypeNameHandling.All,
ContractResolver = new CloudantContractResolver(),
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
我试图像这样反序列化:
var myDeserializedObject = JsonConvert.DeserializeObject<BaseClass>(jsonString, _serializationSettings);
但由于某种原因,我收到了错误
无法创建BaseClass类型的实例。 Type是接口或抽象类,无法实例化。
即使根Json对象确实具有$type
属性。我已尝试反序列化为JObject
,然后使用JObject.To<BaseType>()
,但我的结果相同。我需要使用这种方法,并且不希望使用自定义转换器,因为我在整个地方使用多态。
您对我如何使这种反序列化工作有任何想法吗?
更新10/10/15
我还在调查,我认为问题可能是当我检查我的反序列化对象的JObject
时,第一个属性是_id
属性:
我认为因为错误信息是:
JSON.NET可能需要首先读取类型以实例化正确的对象。我不知道如何从一个单独的项目(如下面提供的项目)中重现_id
首先出现的情况。我尝试了几种嵌套复杂属性的组合,但我总是首先使用$type
。这可能就是它在那里运作良好的原因。
我正试图在ContractResolver上覆盖CreateProperties
:
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;
}
但到目前为止,它对JObject
中我的属性的顺序没有影响。
更新2
好的,所以我设法通过使用:
将$type
属性放在最顶层
var prop = deserializedJObject.Property("$type");
deserializedJObject.Remove("$type");
deserializedJObject.AddFirst(prop);
但不幸的是,它没有帮助,我仍然面临同样的演员问题。
更新3
我能够重现这个问题。如果$type
属性不是 JSON字符串中的第一个属性,则会发生此错误。这显然是一个错误,因为JSON规范表明属性是无序的。
在我的情况下,我不太了解它,因为JSON对象是由始终将_id
置于顶部的数据库返回的。我将在GitHub上记录一个问题,看看我是否可以提出解决方法。
这是一个重现问题的项目:http://we.tl/RiemGkRTF2
答案 0 :(得分:7)
Json.Net 6.0.3解决了这个问题。来自author's blog:
元数据属性处理
某些Json.NET序列化程序功能(如保留类型或引用)需要Json.NET来读取和写入元数据属性,例如: $ type,$ id和$ ref。由于Json.NET反序列化的工作方式,这些元数据属性必须首先在JSON对象中进行排序。这可能会导致问题,因为无法在JavaScript和其他一些JSON框架中对JSON对象属性进行排序。
此版本添加了一个新设置,允许元数据属性位于对象中的任何位置:
MetadataPropertyHandling.ReadAhead
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
添加到您的设置中,这样可以解决您的问题。
答案 1 :(得分:1)
你没有将SerializationSettings传递给你对DeserializeObject的调用,所以它试图在没有TypeNameHandling.All的情况下进行操作。
仅供参考,对于将来阅读的人,这是我的代码:
public abstract class BaseClass
{
public string Key;
}
public class ConcreteClass : BaseClass
{
}
public void TestFoo()
{
ConcreteClass sourceObject = new ConcreteClass (){ Key = "xyz" };
JsonSerializerSettings _serializationSettings = new JsonSerializerSettings ()
{
NullValueHandling = NullValueHandling.Ignore,
TypeNameHandling = TypeNameHandling.All,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
string json = JsonConvert.SerializeObject(sourceObject, _serializationSettings);
Console.Out.WriteLine ("Json is {0}", json);
BaseClass resultObject = JsonConvert.DeserializeObject<BaseClass> (json, _serializationSettings);
Console.Out.WriteLine ("Result is {0}", resultObject);
}