属性为 IEnumable < ISome Interface > 的序列化和反序列化类

我试图移动一些代码来使用 ASP.NET MVC Web API 生成的 Json 数据,而不是 SOAP XML。

类型的属性的序列化和反序列化遇到了一个问题:

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

这个异常消息:

”无法创建类型为 JsonSerializationExample.ISexample 的实例。 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 方法来使序列化再次工作。

有人能帮忙吗?

首先,这是解决问题的“正确”方法吗?

280337 次浏览

在我的项目中,这段代码总是作为默认序列化程序工作,它序列化指定的值,就好像没有特殊的转换器一样:

serializer.Serialize(writer, value);

你不需要使用 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());

文件: < strong > 使用 CustomCreationConverter 反序列化

我来搞定:

显式转换

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

好办法,谢谢! 我用 AndyDBell 的问题和 Cuong Le 的答案构建了一个具有两种不同接口实现的示例:

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

它非常简单,而且对 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 对象初始化器,这对您很重要。

有这个:

public interface ITerm
{
string Name { get; }
}


public class Value : ITerm...


public class Variable : ITerm...


public class Query
{
public IList<ITerm> Terms { get; }
...
}

我设法实现了转换技巧:

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

我解决了这个问题,使用一个特殊的设置为 JsonSerializer 设置,这是所谓的 类型命名处理。所有

TypeNameProcessing 设置包括序列化 JSON 时的类型信息和读取类型信息,以便在反序列化 JSON 时创建创建类型

序列化:

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

你的班级可能有任何类型的基类型字段,它将被正确序列化。

考虑到在大多数情况下,您不希望您的整个数据契约都提供类型,而只提供那些包含抽象或接口或其列表的类型; 并且考虑到这些实例在您的数据实体中非常罕见并且容易识别,最简单和最不冗长的方法是使用

[JsonProperty(ItemTypeNameHandling = TypeNameHandling.Objects)]
public IEnumerable<ISomeInterface> Items { get; set; }

作为属性的属性,包含可枚举/列表/集合。 这将只针对该列表,并且只追加所包含对象的类型信息,如下所示:

{
"Items": [
{
"$type": "Namespace.ClassA, Assembly",
"Property": "Value"
},
{
"$type": "Namespace.ClassB, Assembly",
"Property": "Value",
"Additional_ClassB_Property": 3
}
]
}

简洁、简单,并且位于引入数据模型复杂性的位置,而不是隐藏在某个转换器中。