我将在前言中说我知道问题是什么,我只是不知道如何解决它。我正在与.NET SOA数据层进行通信,该数据层将数据作为JSON返回。一种这样的方法返回一个在其中包含多个集合的对象。该对象基本上如下所示:
{
"Name":"foo",
"widgetCollection":[{"name","foo"}, {"name","foo"},],
"cogCollection": [{"name","foo"}, {"childCogs",<<new collection>>},],
}
我的代表这个对象的类看起来像这样:
public class SuperWidget : IWidget
{
public string Name { get; set; }
public ICollection<IWidget> WidgetCollection { get; set; }
public ICollection<ICog> CogCollection { get; set; }
public SuperWidget()
{
}
[JsonConstructor]
public SuperWidget(IEnumerable<Widget> widgets, IEnumerable<Cog> cogs)
{
WidgetCollection = new Collection<IWidget>();
CogCollection = new Collection<ICog>();
foreach (var w in widgets)
{
WidgetCollection.Add(w);
}
foreach (var c in cogs)
{
CogCollection.Add(c);
}
}
}
这个构造函数工作正常,直到cogCollection添加了一个子集合,现在我收到了上面的错误。具体的cog类看起来像这样:
[Serializable]
public class Cog : ICog
{
public string name { get; set; }
public ICollection<ICog> childCogs { get; set; }
}
我不想将集合更改为具体类型,因为我正在使用IoC。因为我正在使用IoC,我真的想摆脱需要使用具体参数的JsonConstructors,但我还没有想出办法来做到这一点。任何建议将不胜感激!
更新
Yuval Itzchakov建议这个问题可能是重复的,这似乎有些正确(似乎)。在引用的帖子中,页面中的一个答案提供了此处提供的相同解决方案。我没有注意到答案,因为OP的问题与我在这里的问题不同。我的错。-------我的解决方案--------
正如我所说:Matt的解决方案需要一些工作,但我得到了适合我的设置。关于他最初的解决方案,我不喜欢的一件事是这样的线:
return objectType == typeof(ICog);
遵循这种模式,你需要为你通过网络接收的每种抽象类型都有一个JsonConverter。这在我的情况下不太理想,所以我创建了一个通用的JsonConverter:
public class GenericJsonConverter<T>: JsonConverter, IBaseJsonConverter<T>
{
private readonly IUnityContainer Container;
public TalonJsonConverter(IUnityContainer container)
{
Container = container;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(T);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var target = serializer.Deserialize<Newtonsoft.Json.Linq.JObject>(reader);
var result = Container.Resolve<T>();
serializer.Populate(target.CreateReader(), result);
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
然后就在我反序列化数据之前,我做了类似的事情:
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
settings.Converters.Add((JsonConverter)Container.Resolve<IBaseJsonConverter<ICog>>());
Protip:如果你使用Resharper,(JsonConverter)会在这种情况下给你一个可疑的投射警告。
希望其他人在路上找到这个有用的东西!
答案 0 :(得分:25)
您需要向Json.Net提供自定义序列化程序,以告诉它如何处理子嵌入。例如:
var settings = new JsonSerializerSettings();
settings.Converters.Add(new CogConverter());
您的CogConverter
需要继承JsonConverter
并指定CanConvert
您的ICog
界面。也许有些事情如下:
public class CogConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ICog);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize(reader, typeof(Cog));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
在这种情况下,我建议您使用IoC容器注册JsonSerializerSettings
。如果序列化程序不能负责实际构建CogConverter
本身,您可能还需要考虑授予Cog
访问容器的权限。这一切都取决于你的特定架构。
修改强>
进一步阅读后,您似乎可能正在寻找具体如何使用IoC创建的ICog
用于填充。我正在使用以下内容作为我的ReadJson的一部分:
var target = serializer.Deserialize<Newtonsoft.Json.Linq.JObject>(reader);
var objectType = DetermineConcreteType(target);
var result = iocContainer.Resolve(objectType);
serializer.Populate(target.CreateReader(), result);
return result;
这允许您使用任何对象并使用原始JSON填充它,使用您希望在DetermineConcreteType
方法中使用的自定义类型。