如何将json对象反序列化为特定的子类?

我有一个Cabin类,其中包含Row对象的列表。我想像这样序列化对象,但是在反序列化时,我希望Row对象是从Row对象继承的RowRule对象。下面是我一直在尝试的一些示例代码。

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

{

}

回答:

简单的答案是使用a

CustomCreationConverter<Row>RowRule从返回a

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);

}

}

但是,使用TypeNameHandling.Auto意味着"$type"您的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,请注意Newtonsoft文档中的这一警告:

当您的应用程序从外部源反序列化JSON时,应谨慎使用TypeNameHandling。反序列化除None以外的其他值时,应使用自定义SerializationBinder验证传入的类型。

以上是 如何将json对象反序列化为特定的子类? 的全部内容, 来源链接: utcz.com/qa/429996.html

回到顶部