NewtonSoft.Json使用IEnumerable <ISomeInterface>types的属性对类进行序列化和反序列化
我试图移动一些代码来消费ASP.NET MVC Web API生成的Json数据,而不是SOAP Xml。
我遇到了序列化和反序列化types属性的问题:
IEnumerable<ISomeInterface>.
这是一个简单的例子:
public interface ISample{ int SampleId { get; set; } } public class Sample : ISample{ public int SampleId { get; set; } } public class SampleGroup{ public int GroupId { get; set; } public IEnumerable<ISample> Samples { get; set; } } }
我可以轻松地序列化SampleGroup的实例:
var sz = JsonConvert.SerializeObject( sampleGroupInstance );
但是相应的反序列化失败:
JsonConvert.DeserializeObject<SampleGroup>( sz );
有这个例外的消息:
“无法创buildJsonSerializationExample.ISampletypes的实例,Type是一个接口或抽象类,不能被瞬时化。
如果我派生JsonConverter我可以装饰我的财产如下:
[JsonConverter( typeof (SamplesJsonConverter) )] public IEnumerable<ISample> Samples { get; set; }
这是JsonConverter:
public class SamplesJsonConverter : JsonConverter{ public override bool CanConvert( Type objectType ){ return ( objectType == typeof (IEnumerable<ISample>) ); } public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ){ var jA = JArray.Load( reader ); return jA.Select( jl => serializer.Deserialize<Sample>( new JTokenReader( jl ) ) ).Cast<ISample>( ).ToList( ); } public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer ){ ... What works here? } }
这个转换器解决了反序列化的问题,但我不知道如何编写WriteJson方法来获得序列化的工作。
有人可以协助吗?
这是否是解决问题的“正确”方法?
你不需要使用JsonConverterAttribute
,保持你的模型干净,也使用CustomCreationConverter
,代码更简单:
public class SampleConverter : CustomCreationConverter<ISample> { public override ISample Create(Type objectType) { return new Sample(); } }
然后:
var sz = JsonConvert.SerializeObject( sampleGroupInstance ); JsonConvert.DeserializeObject<SampleGroup>( sz, new SampleConverter());
文档: 使用CustomCreationConverter进行反序列化
这是相当简单和开箱即用的json.net提供的支持,你只需要在序列化和反序列化时使用下面的JsonSettings:
JsonConvert.SerializeObject(graph,Formatting.None, new JsonSerializerSettings() { TypeNameHandling =TypeNameHandling.Objects, TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple });
和反序列化使用下面的代码:
JsonConvert.DeserializeObject(Encoding.UTF8.GetString(bData),type, new JsonSerializerSettings(){TypeNameHandling = TypeNameHandling.Objects} );
只需记下JsonSerializerSettings对象初始值设定项,这对您很重要。
我通过为JsonSerializerSettings使用了一个名为TypeNameHandling.All的特殊设置来解决这个问题
TypeNameHandling设置包括序列化JSON时的types信息和读取types信息,以便在反序列化JSON时创build创buildtypes
连载:
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; var text = JsonConvert.SerializeObject(configuration, settings);
反序列化:
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; var configuration = JsonConvert.DeserializeObject<YourClass>(json, settings);
YourClass类可能有任何types的基types字段,它将被正确序列化。
伟大的解决scheme,谢谢! 我采取了AndyDBell的问题和Cuong Le的答案来构build一个具有两个不同接口的实现的例子:
public interface ISample { int SampleId { get; set; } } public class Sample1 : ISample { public int SampleId { get; set; } public Sample1() { } } public class Sample2 : ISample { public int SampleId { get; set; } public String SampleName { get; set; } public Sample2() { } } public class SampleGroup { public int GroupId { get; set; } public IEnumerable<ISample> Samples { get; set; } } class Program { static void Main(string[] args) { //Sample1 instance var sz = "{\"GroupId\":1,\"Samples\":[{\"SampleId\":1},{\"SampleId\":2}]}"; var j = JsonConvert.DeserializeObject<SampleGroup>(sz, new SampleConverter<Sample1>()); foreach (var item in j.Samples) { Console.WriteLine("id:{0}", item.SampleId); } //Sample2 instance var sz2 = "{\"GroupId\":1,\"Samples\":[{\"SampleId\":1, \"SampleName\":\"Test1\"},{\"SampleId\":2, \"SampleName\":\"Test2\"}]}"; var j2 = JsonConvert.DeserializeObject<SampleGroup>(sz2, new SampleConverter<Sample2>()); //Print to show that the unboxing to Sample2 preserved the SampleName's values foreach (var item in j2.Samples) { Console.WriteLine("id:{0} name:{1}", item.SampleId, (item as Sample2).SampleName); } Console.ReadKey(); } }
而一个通用版本的SampleConverter:
public class SampleConverter<T> : CustomCreationConverter<ISample> where T: new () { public override ISample Create(Type objectType) { return ((ISample)new T()); } }
在我的项目中,这段代码总是作为一个默认的序列化器,将指定的值序列化,就好像没有特殊的转换器一样:
serializer.Serialize(writer, value);
我得到这个工作:
明确的转换
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jsonObj = serializer.Deserialize<List<SomeObject>>(reader); var conversion = jsonObj.ConvertAll((x) => x as ISomeObject); return conversion; }
有这样的:
public interface ITerm { string Name { get; } } public class Value : ITerm... public class Variable : ITerm... public class Query { public IList<ITerm> Terms { get; } ... }
我pipe理转换技巧实现:
public class TermConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var field = value.GetType().Name; writer.WriteStartObject(); writer.WritePropertyName(field); writer.WriteValue((value as ITerm)?.Name); writer.WriteEndObject(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jsonObject = JObject.Load(reader); var properties = jsonObject.Properties().ToList(); var value = (string) properties[0].Value; return properties[0].Name.Equals("Value") ? (ITerm) new Value(value) : new Variable(value); } public override bool CanConvert(Type objectType) { return typeof (ITerm) == objectType || typeof (Value) == objectType || typeof (Variable) == objectType; } }
它允许我在JSON中序列化和反序列化,如:
string JsonQuery = "{\"Terms\":[{\"Value\":\"This is \"},{\"Variable\":\"X\"},{\"Value\":\"!\"}]}"; ... var query = new Query(new Value("This is "), new Variable("X"), new Value("!")); var serializeObject = JsonConvert.SerializeObject(query, new TermConverter()); Assert.AreEqual(JsonQuery, serializeObject); ... var queryDeserialized = JsonConvert.DeserializeObject<Query>(JsonQuery, new TermConverter());