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);
}
}
}
我们有一个对象列表,我们希望将其序列化为 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);
}
}
}