C# 在版本/格式之间迁移序列化Json.NET文档的策略
我正在使用Json.Net序列化一些应用程序数据。当然,应用程序规范略有变化,我们需要重构一些业务对象数据。将以前序列化的数据迁移到新的数据格式有哪些可行的策略 例如,假设我们最初有一个业务对象,如:C# 在版本/格式之间迁移序列化Json.NET文档的策略,c#,json.net,versioning,C#,Json.net,Versioning,我正在使用Json.Net序列化一些应用程序数据。当然,应用程序规范略有变化,我们需要重构一些业务对象数据。将以前序列化的数据迁移到新的数据格式有哪些可行的策略 例如,假设我们最初有一个业务对象,如: public class Owner { public string Name {get;set;} } public class LeaseInstrument { public ObservableCollection<Owner> OriginalLessees
public class Owner
{
public string Name {get;set;}
}
public class LeaseInstrument
{
public ObservableCollection<Owner> OriginalLessees {get;set;}
}
我的意思是,不开玩笑,Json.Net,这就是为什么我在反序列化这些对象时尝试运行JsonConverter,以便手动处理序列化类型与编译类型不匹配的事实
值得一提的是,以下是我们正在使用的JsonSerializerSettings:
var settings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ContractResolver = new WritablePropertiesOnlyResolver(),
TypeNameHandling = TypeNameHandling.All,
ObjectCreationHandling = ObjectCreationHandling.Reuse
};
您有以下问题:
TypeNameHandling.All进行了序列化。此设置序列化集合和对象的类型信息。我不建议这样做。相反,我建议使用TypeNameHandling.Objects
,然后让反序列化系统选择集合类型
也就是说,要处理现有的JSON,您可以调整IgnoreArrayTypeConverter
from以用于可调整大小的集合:
public class IgnoreCollectionTypeConverter : JsonConverter
{
public IgnoreCollectionTypeConverter() { }
public IgnoreCollectionTypeConverter(Type ItemConverterType)
{
this.ItemConverterType = ItemConverterType;
}
public Type ItemConverterType { get; set; }
public override bool CanConvert(Type objectType)
{
// TODO: test with read-only collections.
return objectType.GetCollectItemTypes().Count() == 1 && !objectType.IsDictionary() && !objectType.IsArray;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (!CanConvert(objectType))
throw new JsonSerializationException(string.Format("Invalid type \"{0}\"", objectType));
if (reader.TokenType == JsonToken.Null)
return null;
var token = JToken.Load(reader);
var itemConverter = (ItemConverterType == null ? null : (JsonConverter)Activator.CreateInstance(ItemConverterType, true));
if (itemConverter != null)
serializer.Converters.Add(itemConverter);
try
{
return ToCollection(token, objectType, existingValue, serializer);
}
finally
{
if (itemConverter != null)
serializer.Converters.RemoveLast(itemConverter);
}
}
private static object ToCollection(JToken token, Type collectionType, object existingValue, JsonSerializer serializer)
{
if (token == null || token.Type == JTokenType.Null)
return null;
else if (token.Type == JTokenType.Array)
{
// Here we assume that existingValue already is of the correct type, if non-null.
existingValue = serializer.DefaultCreate<object>(collectionType, existingValue);
token.PopulateObject(existingValue, serializer);
return existingValue;
}
else if (token.Type == JTokenType.Object)
{
var values = token["$values"];
if (values == null)
return null;
return ToCollection(values, collectionType, existingValue, serializer);
}
else
{
throw new JsonSerializationException("Unknown token type: " + token.ToString());
}
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public static class JsonExtensions
{
public static T DefaultCreate<T>(this JsonSerializer serializer, Type objectType, object existingValue)
{
if (serializer == null)
throw new ArgumentNullException();
if (existingValue is T)
return (T)existingValue;
return (T)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
}
public static void PopulateObject(this JToken obj, object target, JsonSerializer serializer)
{
if (target == null)
throw new NullReferenceException();
if (obj == null)
return;
using (var reader = obj.CreateReader())
serializer.Populate(reader, target);
}
}
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> GetCollectItemTypes(this Type type)
{
foreach (Type intType in type.GetInterfacesAndSelf())
{
if (intType.IsGenericType
&& intType.GetGenericTypeDefinition() == typeof(ICollection<>))
{
yield return intType.GetGenericArguments()[0];
}
}
}
public static bool IsDictionary(this Type type)
{
if (typeof(IDictionary).IsAssignableFrom(type))
return true;
foreach (Type intType in type.GetInterfacesAndSelf())
{
if (intType.IsGenericType
&& intType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
{
return true;
}
}
return false;
}
}
public static class ListExtensions
{
public static bool RemoveLast<T>(this IList<T> list, T item)
{
if (list == null)
throw new ArgumentNullException();
var comparer = EqualityComparer<T>.Default;
for (int i = list.Count - 1; i >= 0; i--)
{
if (comparer.Equals(list[i], item))
{
list.RemoveAt(i);
return true;
}
}
return false;
}
}
如果您不想在数据模型中依赖Json.NET,可以在自定义合同解析程序中执行此操作:
public class WritablePropertiesOnlyResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var result = base.CreateProperty(member, memberSerialization);
if (typeof(LeaseInstrument).IsAssignableFrom(result.DeclaringType) && typeof(ICollection<LeaseOwner>).IsAssignableFrom(result.PropertyType))
{
var converter = new IgnoreCollectionTypeConverter { ItemConverterType = typeof(OwnerToLeaseOwnerConverter) };
result.Converter = result.Converter ?? converter;
result.MemberConverter = result.MemberConverter ?? converter;
}
return result;
}
}
公共类WritablePropertiesOnlyResolver:DefaultContractResolver
{
受保护的重写JsonProperty CreateProperty(MemberInfo成员、MemberSerialization MemberSerialization)
{
var result=base.CreateProperty(成员,成员序列化);
if(typeof(LeaseInstrument).IsAssignableFrom(result.DeclaringType)和&typeof(ICollection).IsAssignableFrom(result.PropertyType))
{
var converter=new IgnoreCollectionTypeConverter{ItemConverterType=typeof(OwnerToLeaseOwnerConverter)};
结果转换器=结果转换器??转换器;
result.MemberConverter=result.MemberConverter??converter;
}
返回结果;
}
}
顺便说一句,您可能希望您可能会发现我们的库Migrations.Json.Net很有帮助 一个简单的例子。假设你从一节课开始
public class Person {
public string Name {get;set}
}
然后你想迁移到
public class Person {
public string FirstName {get;set}
public string SecondName {get;set}
public string Name => $"{FirstName} {SecondName}";
}
您可能会执行以下迁移
public class Person {
public string FirstName {get;set}
public string SecondName {get;set}
public string Name => $"{FirstName} {SecondName}";
public void migrate_1(JToken token, JsonSerializer s){
var name = token["Name"];
var names = names.Split(" ");
token["FirstName"] = names[0];
token["SecondName"] = names[1];
return token;
}
}
上述内容掩盖了一些细节,但在该项目的主页上有一个完整的例子。我们在两个生产项目中广泛使用此功能。主页上的示例有13次迁移到一个复杂对象,该对象在几年内发生了变化 你能提供一些关于你的JSON字符串的例子吗?谢谢你的建议和信息!粗略地看,这似乎能解决我们的问题。明天,当我有一些带宽来解决这个问题时,我会让你知道它是否有效,并相应地标记你的答案。再次感谢。我添加了:if(objectType==typeof(LeaseOwner)){var thiscoverter=serializer.converts.Single(x=>x.GetType()==typeof(OwnerToLeaseOwnerConverter));serializer.converter.Remove(thiscoverter);var obj=serializer.Deserialize(reader);serializer.converts.Add(thiscoverter);return obj;}位于ReadJson方法顶部附近,用于处理所有者已被转换的情况。这看起来很笨重,我觉得我错过了一些明显的东西。有什么建议吗?(抱歉,我无法将其格式化)@D.Reagan-不确定我是否在跟踪。。。等等,您是否将
OwnerToLeaseOwnerConverter
添加到转换器的全局列表中,而不是添加到需要它的特定属性中?因为你发现的原因,那是行不通的。这真的有必要吗?@D.Reagan=还有,什么是LeaseOriginalOwner
?这不在你的问题之内。你的问题似乎变得越来越复杂,把你的问题分解成单独的小问题会更好吗?主页上有一个完整的例子,单元测试用例可用
public class WritablePropertiesOnlyResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var result = base.CreateProperty(member, memberSerialization);
if (typeof(LeaseInstrument).IsAssignableFrom(result.DeclaringType) && typeof(ICollection<LeaseOwner>).IsAssignableFrom(result.PropertyType))
{
var converter = new IgnoreCollectionTypeConverter { ItemConverterType = typeof(OwnerToLeaseOwnerConverter) };
result.Converter = result.Converter ?? converter;
result.MemberConverter = result.MemberConverter ?? converter;
}
return result;
}
}
public class Person {
public string Name {get;set}
}
public class Person {
public string FirstName {get;set}
public string SecondName {get;set}
public string Name => $"{FirstName} {SecondName}";
}
public class Person {
public string FirstName {get;set}
public string SecondName {get;set}
public string Name => $"{FirstName} {SecondName}";
public void migrate_1(JToken token, JsonSerializer s){
var name = token["Name"];
var names = names.Split(" ");
token["FirstName"] = names[0];
token["SecondName"] = names[1];
return token;
}
}