Подтвердить что ты не робот

Тип - это интерфейс или абстрактный класс и не может быть создан

Я предоставлю это, сказав, что я знаю, в чем проблема, я просто не знаю, как ее решить. Я общаюсь с уровнем данных SOA.NET, который возвращает данные как 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, которые принимают конкретные параметры, но я не понял способ сделать это. Любые советы будут очень признательны!

Update:

Юваль Ицчаков предполагает, что этот вопрос, вероятно, дубликат, несколько верен (кажется). В указанной ссылке один из ответов на странице содержит то же решение, которое было предоставлено здесь. Я не заметил этого ответа, поскольку вопрос с ОП был другим, чем тот, который у меня был здесь. Виноват.

------- мое решение --------

Как я уже сказал: решение 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), вы получите подозрительное предупреждение о броске в этом сценарии.

Надеюсь, кто-то найдет это полезным в будущем!

4b9b3361

Ответ 1

Вам нужно будет предоставить пользовательский сериализатор 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);
    }
}

Я бы рекомендовал зарегистрировать ваш JsonSerializerSettings с вашим контейнером IoC в этом случае. Возможно, вы захотите рассмотреть возможность предоставления CogConverter доступа к контейнеру, если сериализатор не может нести ответственность за фактическое построение самого Cog; что все зависит от вашей конкретной архитектуры.

Edit

При дальнейшем чтении кажется, что вы, возможно, ищете конкретно, как использовать созданный 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.