JSON.net 将非类型化列表序列化为类型化列表?

JSON.net serializing untyped list as typed list?

我们有一个对象列表,我们希望将其序列化为 json 字符串。 这些对象各有一个 属性,这是一个无类型的 ICollection。 问题是,我们想反序列化 JSON 并将其与另一个列表进行比较,在序列化时,我们有信息,它是什么类型。

由于我们无法将 属性 更改为类型列表,是否可以通过某种方式告诉 JSON.NET:"It's untyped, but serialize it like it is typed of Type T?"

我想,当反序列化和某种方式传递类型时,我可能会转换它,但这会非常混乱。

编辑:我目前使用的是将数据从 JSON 转换为预期类型的​​混乱方式:

    private static void CastAssertDataSources(ReportDataSource dataSourceFromDb, ReportDataSource dataSourceFromJson)
    {
        var dtoType = dataSourceFromDb.Data.GetType().GetElementType();

        var dtosFromJson = new ArrayList(dataSourceFromJson.Data);
        ArrayList typedJsonDtos = new ArrayList();

        for (int i = 0; i < dataSourceFromJson.Data.Count; i++)
        {
            var jsonDto = dtosFromJson[i];
            var containerJsonDto = (JContainer)jsonDto;
            var typedJsonDto = containerJsonDto.ToObject(dtoType);
            typedJsonDtos.Add(typedJsonDto);
        }

        dataSourceFromJson = new ReportDataSource(dataSourceFromJson.Name, typedJsonDtos);
        dataSourceFromDb.AssertIsEqualTo(dataSourceFromJson);
    }

"AssertisEqualTo" 是我们的扩展,但我想这应该无关紧要。

假设您的 class 看起来像这样:

public class ReportDataSource 
{
    public string Name { get; set; }
    public ICollection Data { get; set; }
}

你可以用合适的方法来做 JsonConverter:

public sealed class TypedToTypelessCollectionConverter : JsonConverter
{
    [ThreadStatic]
    static Type itemType;

    public static IDisposable SetItemType(Type deserializedType)
    {
        return new ItemType(deserializedType);
    }

    sealed class ItemType : IDisposable
    {
        Type oldType;

        internal ItemType(Type type)
        {
            this.oldType = itemType;
            itemType = type;
        }

        int disposed = 0;

        public void Dispose()
        {
            // Dispose of unmanaged resources.
            if (Interlocked.Exchange(ref disposed, 1) == 0)
            {
                // Free any other managed objects here.
                itemType = oldType;
                oldType = null;
            }
            // Suppress finalization.  Since this class actually has no finalizer, this does nothing.
            GC.SuppressFinalize(this);
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(ICollection);
    }

    public override bool CanWrite { get { return false; }}

    public override bool CanRead { get { return itemType != null; } }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return serializer.Deserialize(reader, typeof(List<>).MakeGenericType(itemType));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

public static class TypeExtensions
{
    /// <summary>
    /// Return all interfaces implemented by the incoming type as well as the type itself if it is an interface.
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public static IEnumerable<Type> GetInterfacesAndSelf(this Type type)
    {
        if (type == null)
            throw new ArgumentNullException();
        if (type.IsInterface)
            return new[] { type }.Concat(type.GetInterfaces());
        else
            return type.GetInterfaces();
    }

    public static IEnumerable<Type> GetEnumerableTypes(this Type type)
    {
        foreach (Type intType in type.GetInterfacesAndSelf())
        {
            if (intType.IsGenericType
                && intType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            {
                yield return intType.GetGenericArguments()[0];
            }
        }
    }
}

然后像这样使用它:

public class ReportDataSource 
{
    public string Name { get; set; }

    [JsonConverter(typeof(TypedToTypelessCollectionConverter))]
    public ICollection Data { get; set; }

    public static ReportDataSource Deserialize(ReportDataSource dataSourceFromDb, string json)
    {
        using (TypedToTypelessCollectionConverter.SetItemType(dataSourceFromDb == null || dataSourceFromDb.Data == null ? null : dataSourceFromDb.Data.GetType().GetEnumerableTypes().SingleOrDefault()))
        {
            return JsonConvert.DeserializeObject<ReportDataSource>(json);
        }
    }
}