我先说一下,我知道问题是什么,我只是不知道如何解决它。我正在与一个. 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添加子集合之前,这个构造函数工作得很好,现在我得到了上面的错误。一个具体的齿轮类是这样的:
[Serializable]
public class Cog : ICog
{
public string name { get; set; }
public ICollection<ICog> childCogs { get; set; }
}
我不想将集合更改为具体类型,因为我正在使用IoC。因为我正在使用IoC,我真的很想摆脱需要JsonConstructors接受具体参数,但我还没有找到一种方法来做到这一点。任何建议将非常感激!
更新:
Yuval Itzchakov关于这个问题可能是重复的建议在某种程度上是正确的(看起来)。在引用的帖子中,页面下方的一个答案提供了与这里提供的相同的解决方案。我没有注意到这个答案,因为OP的问题和我在这里的问题不同。我的错误。
——我的解决方案 --------
正如我所说:马特的解决方案花了一点功夫,但我找到了一些适合我的东西。我不喜欢他最初的解决方案的一点是,像这样的行:
return objectType == typeof(ICog);
按照这种模式,您需要为通过网络接收的每个抽象类型提供一个JsonConverter。这在我的情况下是不太理想的,所以我创建了一个通用的JsonConverter:
public class GenericJsonConverter<T>: JsonConverter, IBaseJsonConverter<T>
{
private readonly IUnityContainer Container;
public GenericJsonConverter(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)会在这种情况下给你一个可疑的强制转换警告。
希望以后有人会发现这有用!
您需要为Json提供一个自定义序列化器。网告诉它如何处理孩子的齿轮。例如:
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
。如果序列化器不能实际负责构造Cog
,你也可以考虑给CogConverter
访问容器的权限;这完全取决于您的特定架构。
编辑
进一步阅读后,似乎您可能正在寻找具体如何使用为人口创建的ICog
的IoC。我使用以下内容作为我的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
方法中使用您希望的自定义类型。