如果$ type不是对象中的第一个属性,那么JSON中的类型元数据显然不受尊重

tob*_*777 2 c# json.net

我仔细研究了这个问题,最接近我的情况并未解决我的担忧.

我有以下课程:

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

Bri*_*ers 8

这个问题在Json.Net 6.0.3中得到了解决.来自作者的博客:

元数据属性处理

一些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
Run Code Online (Sandbox Code Playgroud)

在内部,此设置将指示序列化程序将整个JSON对象加载到内存中.然后将从对象中读取元数据属性,然后反复序列化将继续正常进行.内存使用和速度有一点点成本,但如果您需要使用元数据属性的功能并且无法保证JSON对象属性顺序,那么您会发现这很有用.

最重要的是,添加MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead到您的设置,这应该可以解决您的问题.