我有一个包含Row对象列表的Cabin类。我希望像这样序列化对象,但是在反序列化时,我希望Row对象是RowRule对象,它继承自Row对象。以下是我尝试过的一些示例代码。
class Program
{
static void Main(string[] args)
{
var cabin = new Cabin();
var row = new Row();
row.Status = "Success";
cabin.Rows = new List<Row>()
{
row,
row
};
JsonSerializerSettings settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto
};
string json = JsonConvert.SerializeObject(cabin, Formatting.Indented, settings);
Console.WriteLine(json);
Cabin obj = JsonConvert.DeserializeObject<Cabin>(json,
new JsonSerializerSettings() {TypeNameHandling = TypeNameHandling.Auto});
Console.WriteLine(obj);
Debug.Assert(obj.Rows.First().GetType().Name == "RowRule");
}
}
class Cabin
{
public IList<Row> Rows { get; set; }
}
class Row
{
public string Status { get; set; }
}
class RowRule : Row
{
}
答案 0 :(得分:3)
简单的答案是使用CustomCreationConverter<Row>
并从Create()
返回class RowToRoleRuleConverter : CustomCreationConverter<Row>
{
public override Row Create(Type objectType)
{
if (objectType.IsAssignableFrom(typeof(RowRule)))
return Activator.CreateInstance<RowRule>();
return (Row)Activator.CreateInstance(objectType);
}
}
:
"$type"
但是,您使用的是TypeNameHandling.Auto
,这意味着您的JSON中可能存在多态CustomCreationConverter<T>
属性。不幸的是,DowncastingConverter<TBase, TDerived>
忽略了这些属性。因此,有必要做一些额外的工作并创建public class DowncastingConverter<TBase, TDerived> : PolymorphicCreationConverter<TBase> where TDerived : TBase
{
protected override TBase Create(Type objectType, Type polymorphicType, object existingValue, IContractResolver contractResolver, JObject obj)
{
Type createType = objectType;
if (createType.IsAssignableFrom(polymorphicType))
createType = polymorphicType;
if (createType.IsAssignableFrom(typeof(TDerived)))
createType = typeof(TDerived);
if (existingValue != null && createType.IsAssignableFrom(existingValue.GetType()))
return (TBase)existingValue;
var contract = contractResolver.ResolveContract(createType);
return (TBase)contract.DefaultCreator();
}
}
public abstract class PolymorphicCreationConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException("CustomCreationConverter should only be used while deserializing.");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var obj = JObject.Load(reader);
Type polymorphicType = null;
var polymorphicTypeString = (string)obj["$type"];
if (polymorphicTypeString != null)
{
if (serializer.TypeNameHandling != TypeNameHandling.None)
{
string typeName, assemblyName;
ReflectionUtils.SplitFullyQualifiedTypeName(polymorphicTypeString, out typeName, out assemblyName);
polymorphicType = serializer.Binder.BindToType(assemblyName, typeName);
}
obj.Remove("$type");
}
var value = Create(objectType, polymorphicType, existingValue, serializer.ContractResolver, obj);
if (value == null)
throw new JsonSerializationException("No object created.");
using (var subReader = obj.CreateReader())
serializer.Populate(subReader, value);
return value;
}
protected abstract T Create(Type objectType, Type polymorphicType, object existingValue, IContractResolver iContractResolver, JObject obj);
public override bool CanWrite { get { return false; } }
}
internal static class ReflectionUtils
{
// Utilities taken from https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs
// I couldn't find a way to access these directly.
public static void SplitFullyQualifiedTypeName(string fullyQualifiedTypeName, out string typeName, out string assemblyName)
{
int? assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName);
if (assemblyDelimiterIndex != null)
{
typeName = fullyQualifiedTypeName.Substring(0, assemblyDelimiterIndex.GetValueOrDefault()).Trim();
assemblyName = fullyQualifiedTypeName.Substring(assemblyDelimiterIndex.GetValueOrDefault() + 1, fullyQualifiedTypeName.Length - assemblyDelimiterIndex.GetValueOrDefault() - 1).Trim();
}
else
{
typeName = fullyQualifiedTypeName;
assemblyName = null;
}
}
private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName)
{
int scope = 0;
for (int i = 0; i < fullyQualifiedTypeName.Length; i++)
{
char current = fullyQualifiedTypeName[i];
switch (current)
{
case '[':
scope++;
break;
case ']':
scope--;
break;
case ',':
if (scope == 0)
{
return i;
}
break;
}
}
return null;
}
}
:
JsonSerializerSettings readSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto,
Converters = new[] { new DowncastingConverter<Row, RowRule>() },
};
Cabin obj = JsonConvert.DeserializeObject<Cabin>(json, readSettings);
然后使用它:
TypeNameHandling
原型fiddle。
最后,在使用UIButton
时,请注意Newtonsoft docs中的这一注意事项:
当您的应用程序从外部源反序列化JSON时,应谨慎使用TypeNameHandling。使用非None以外的值进行反序列化时,应使用自定义SerializationBinder验证传入类型。
有关可能需要执行此操作的讨论,请参阅 TypeNameHandling caution in Newtonsoft Json 。
答案 1 :(得分:0)
示例代码的问题是,您正在创建Row
的对象并尝试获取RowRule
这是不可能的。
可能你想这样做:
var cabin = new Cabin();
var row = new RowRule(); // create derived object
row.Status = "Success";
cabin.Rows = new List<Row>()
{
row,
row
};