tme*_*ser 116 .net c# json json.net
我正在尝试建立一个读取器,它将从各种网站中获取JSON对象(想想信息抓取)并将它们转换为C#对象.我目前正在使用JSON.NET进行反序列化过程.我遇到的问题是它不知道如何处理类中的接口级属性.所以有些本质:
public IThingy Thing
Run Code Online (Sandbox Code Playgroud)
会产生错误:
无法创建IThingy类型的实例.Type是接口或抽象类,无法实例化.
让它成为ITINGy而不是Thingy是相对重要的,因为我正在处理的代码被认为是敏感的,单元测试非常重要.对于像Thingy这样的完全成熟的对象,不可能对原子测试脚本的对象进行模拟.它们必须是一个接口.
我一直在研究JSON.NET的文档已有一段时间了,我在这个网站上找到的与此相关的问题都来自一年多以前.有帮助吗?
此外,如果重要,我的应用程序是用.NET 4.0编写的.
Mar*_*uer 102
@SamualDavis在相关问题中提供了一个很好的解决方案,我将在此总结.
如果必须将JSON流反序列化为具有接口属性的具体类,则可以将具体类作为参数包含在类的构造函数中! NewtonSoft反序列化器足够聪明,可以确定它需要使用这些具体类来反序列化属性.
这是一个例子:
public class Visit : IVisit
{
/// <summary>
/// This constructor is required for the JSON deserializer to be able
/// to identify concrete classes to use when deserializing the interface properties.
/// </summary>
public Visit(MyLocation location, Guest guest)
{
Location = location;
Guest = guest;
}
public long VisitId { get; set; }
public ILocation Location { get; set; }
public DateTime VisitDate { get; set; }
public IGuest Guest { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
Ste*_*rex 50
(从这个问题复制)
如果我没有控制传入的JSON(因此无法确保它包含$ type属性),我编写了一个自定义转换器,只允许您显式指定具体类型:
public class Model
{
[JsonConverter(typeof(ConcreteTypeConverter<Something>))]
public ISomething TheThing { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
这只是使用Json.Net中的默认序列化器实现,同时明确指定具体类型.
public class ConcreteTypeConverter<TConcrete> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
//assume we can convert to anything for now
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//explicitly specify the concrete type we want to create
return serializer.Deserialize<TConcrete>(reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//use the default serialization - it works fine
serializer.Serialize(writer, value);
}
}
Run Code Online (Sandbox Code Playgroud)
Maf*_*fii 43
为什么要使用转换器?有一个本机功能Newtonsoft.Json
来解决这个确切的问题:
设置TypeNameHandling
在JsonSerializerSettings
以TypeNameHandling.Auto
JsonConvert.SerializeObject(
toSerialize,
new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto
});
Run Code Online (Sandbox Code Playgroud)
这会将每个类型放入json中,这不是作为类型的具体实例而是作为接口或抽象类保存.
我测试了它,它就像一个魅力,即使有列表.
来源和替代手动实施:代码内部博客
Eri*_*dil 36
要启用多个接口实现的反序列化,可以使用JsonConverter,但不能通过属性:
Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
serializer.Converters.Add(new DTOJsonConverter());
Interfaces.IEntity entity = serializer.Deserialize(jsonReader);
Run Code Online (Sandbox Code Playgroud)
DTOJsonConverter使用具体实现映射每个接口:
class DTOJsonConverter : Newtonsoft.Json.JsonConverter
{
private static readonly string ISCALAR_FULLNAME = typeof(Interfaces.IScalar).FullName;
private static readonly string IENTITY_FULLNAME = typeof(Interfaces.IEntity).FullName;
public override bool CanConvert(Type objectType)
{
if (objectType.FullName == ISCALAR_FULLNAME
|| objectType.FullName == IENTITY_FULLNAME)
{
return true;
}
return false;
}
public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
{
if (objectType.FullName == ISCALAR_FULLNAME)
return serializer.Deserialize(reader, typeof(DTO.ClientScalar));
else if (objectType.FullName == IENTITY_FULLNAME)
return serializer.Deserialize(reader, typeof(DTO.ClientEntity));
throw new NotSupportedException(string.Format("Type {0} unexpected.", objectType));
}
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
Run Code Online (Sandbox Code Playgroud)
只有解串器才需要DTOJsonConverter.序列化过程保持不变.Json对象不需要嵌入具体类型名称.
这个SO帖子提供了与通用JsonConverter相同的解决方案.
Gil*_*dor 14
使用此类,将抽象类型映射到实际类型:
public class AbstractConverter<TReal, TAbstract> : JsonConverter where TReal : TAbstract
{
public override Boolean CanConvert(Type objectType)
=> objectType == typeof(TAbstract);
public override Object ReadJson(JsonReader reader, Type type, Object value, JsonSerializer jser)
=> jser.Deserialize<TReal>(reader);
public override void WriteJson(JsonWriter writer, Object value, JsonSerializer jser)
=> jser.Serialize(writer, value);
}
Run Code Online (Sandbox Code Playgroud)
...当反序列化时:
var settings = new JsonSerializerSettings
{
Converters = {
new AbstractConverter<Thing, IThingy>(),
new AbstractConverter<Thing2, IThingy2>()
},
};
JsonConvert.DeserializeObject(json, type, settings);
Run Code Online (Sandbox Code Playgroud)
小智 8
对于那些可能对 Oliver 引用的 ConcreteListTypeConverter 感到好奇的人,这是我的尝试:
public class ConcreteListTypeConverter<TInterface, TImplementation> : JsonConverter where TImplementation : TInterface
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var res = serializer.Deserialize<List<TImplementation>>(reader);
return res.ConvertAll(x => (TInterface) x);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
Run Code Online (Sandbox Code Playgroud)
任何对象都不会 是IThingy,因为接口根据定义都是抽象的。
首先序列化的对象是某种具体类型,实现了抽象接口。您需要让同一个具体类恢复序列化数据。
生成的对象将是某种实现您正在寻找的抽象接口的类型。
从文档中可以看出,您可以使用
(Thingy)JsonConvert.DeserializeObject(jsonString, typeof(Thingy));
Run Code Online (Sandbox Code Playgroud)
反序列化时通知 JSON.NET 有关具体类型的信息。
你可能会尝试两件事:
实现try/parse模型:
public class Organisation {
public string Name { get; set; }
[JsonConverter(typeof(RichDudeConverter))]
public IPerson Owner { get; set; }
}
public interface IPerson {
string Name { get; set; }
}
public class Tycoon : IPerson {
public string Name { get; set; }
}
public class Magnate : IPerson {
public string Name { get; set; }
public string IndustryName { get; set; }
}
public class Heir: IPerson {
public string Name { get; set; }
public IPerson Benefactor { get; set; }
}
public class RichDudeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(IPerson));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// pseudo-code
object richDude = serializer.Deserialize<Heir>(reader);
if (richDude == null)
{
richDude = serializer.Deserialize<Magnate>(reader);
}
if (richDude == null)
{
richDude = serializer.Deserialize<Tycoon>(reader);
}
return richDude;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// Left as an exercise to the reader :)
throw new NotImplementedException();
}
}
Run Code Online (Sandbox Code Playgroud)
或者,如果您可以在对象模型中执行此操作,请在IPerson和叶对象之间实现具体的基类,并对其进行反序列化.
第一个可能在运行时失败,第二个需要更改对象模型并将输出均匀化到最小公分母.
我发现这很有用.你可能也是.
示例用法
public class Parent
{
[JsonConverter(typeof(InterfaceConverter<IChildModel, ChildModel>))]
IChildModel Child { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
定制创作转换器
public class InterfaceConverter<TInterface, TConcrete> : CustomCreationConverter<TInterface>
where TConcrete : TInterface, new()
{
public override TInterface Create(Type objectType)
{
return new TConcrete();
}
}
Run Code Online (Sandbox Code Playgroud)
Nicholas Westby在一篇很棒的文章中提供了一个很好的解决方案。
如果要将JSON反序列化为实现这样的接口的许多可能的类之一:
public class Person
{
public IProfession Profession { get; set; }
}
public interface IProfession
{
string JobTitle { get; }
}
public class Programming : IProfession
{
public string JobTitle => "Software Developer";
public string FavoriteLanguage { get; set; }
}
public class Writing : IProfession
{
public string JobTitle => "Copywriter";
public string FavoriteWord { get; set; }
}
public class Samples
{
public static Person GetProgrammer()
{
return new Person()
{
Profession = new Programming()
{
FavoriteLanguage = "C#"
}
};
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用自定义JSON转换器:
public class ProfessionConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(IProfession);
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
throw new InvalidOperationException("Use default serialization.");
}
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue,
JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var profession = default(IProfession);
switch (jsonObject["JobTitle"].Value())
{
case "Software Developer":
profession = new Programming();
break;
case "Copywriter":
profession = new Writing();
break;
}
serializer.Populate(jsonObject.CreateReader(), profession);
return profession;
}
}
Run Code Online (Sandbox Code Playgroud)
您将需要使用JsonConverter属性装饰“ Profession”属性,以使其知道使用自定义转换器:
public class Person
{
[JsonConverter(typeof(ProfessionConverter))]
public IProfession Profession { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用接口强制转换类:
Person person = JsonConvert.DeserializeObject<Person>(jsonString);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
111400 次 |
最近记录: |