Создать дочерний тип во время десериализации

Я получил следующий документ JSON:

{
    "Title": "jkdjdjd",
    "Description": "dkfkkdd",
    "Actions": [{
                    "ActionType": "Email",
                    "Subject": "Bkdfkdk",
                    "Body": "kddkdkkd"
                }, {
                    "ActionType": "SMS",
                    "PhoneNumber": "+46333333"
                }
    ]
}

Мои классы выглядят так:

public class Trigger
{
    public string Name { get; set; }
    public string Description { get; set; }
    public List<Action> Actions { get; set; }
}


public class Action
{
    public string ActionType { get; set; }
}

public class EmailAction : Action
{
    public string Subject { get; set; }
    public string Body { get; set; }
}

public class SmsAction : Action
{
    public string PhoneNumber { get; set; }
}

Итак, я в основном хочу, чтобы JSON.NET выбирал тип подкласса в зависимости от имени в «ActionType». Я знаю, что JSON.NET поддерживает специальное поле, которое можно использовать для идентификации подкласса. Но я предпочитаю, чтобы понятное имя определяло, какой класс генерировать.

Я понял, что должен использовать CustomCreationConverter<Action> для выбора. Но я не могу понять, как читать это поле, не испортив реальную десериализацию.

Если это поможет, я мог бы использовать следующий макет:

public class Action
{
    public string ActionType { get; set; }
    public ActionData Data { get; set; }
}

public ActionData
{
}

public class EmailData : ActionData
{
    public string Subject { get; set; }
    public string Body { get; set; }
}

public class SmsData : ActionData
{
    public string PhoneNumber { get; set; }
}

то есть JSON будет:

{
    "Title": "jkdjdjd",
    "Description": "dkfkkdd",
    "Actions": [{
                    "ActionType": "Email",
                    "Data": {
                        "Subject": "Bkdfkdk",
                        "Body": "kddkdkkd"
                    }
                }, {
                    "ActionType": "SMS",
                    "Data": {
                        "PhoneNumber": "+46333333"
                    }
                }
    ]
}

person jgauffin    schedule 22.02.2014    source источник
comment
Не отвечая на ваш вопрос, но в корневом узле вашего JSON у вас есть свойство Title, а ваш класс Trigger имеет свойство Name. Я думаю, что они должны иметь одно и то же имя свойства.   -  person Kevin Brechbühl    schedule 23.02.2014


Ответы (1)


Если вас не беспокоит наличие свойства type, включенного в JSON, вы можете использовать настройку JsonSerializer TypeNameHandling = TypeNameHandling.Auto.

В противном случае вы можете создать собственный JsonConverter и добавить его в список конвертеров, используемых во время сериализации. В этом посте есть хороший пример, который хорошо сработал для меня:

http://dotnetbyexample.blogspot.co.uk/2012/02/json-deserialization-with-jsonnet-class.html

ИЗМЕНИТЬ:

Это не работает для вас, как ожидалось? Вы пытались сделать базовый класс Action абстрактным?

Что вы пробовали с точки зрения CreationConverter? Как именно это не работает - можете ли вы рассказать об этом подробнее?

public class JsonActionConverter : JsonCreationConverter<Action>
  {
    protected override Action Create(Type objectType, JObject jsonObject)
    {
      var typeName = jsonObject["ActionType"].ToString();
      switch(typeName)
      {
        case "Email":
          return new EmailAction();
        case "SMS":
          return new SMSAction();
        default: return null;
      }
    }
  }
person Joanna Derks    schedule 22.02.2014
comment
Просто напишите JsonConverter, необходимое для завершения ответа. В противном случае это просто комментарий. - person L.B; 23.02.2014
comment
Прочитайте середину моего вопроса. Я уже обратился к тому, что вы говорите - person jgauffin; 23.02.2014
comment
@jgauffin - посмотрите мое редактирование и опубликуйте некоторые подробности об ошибках / неожиданном поведении, которые вы получаете, если это не решит проблему. - person Joanna Derks; 23.02.2014