У меня есть приложение на основе задач, которому необходимо вводить информацию в некоторые задачи. Задачи могут быть клонированы или сохранены в файле сохранения, в каждом случае класс сериализуется как JSON. Информация о приложении, переданная задачам, не сохраняется, поскольку она сохраняет только сеанс приложения.
public interface IApplicationData { }
public class ApplicationData : IApplicationData { }
public interface ITask {
IApplicationData Data { get; }
}
[DataContract]
public abstract class Task : ITask, ICloneable {
protected Task(IApplicationData data = null) {
Data = data;
}
public IApplicationData Data { get; }
public object Clone() {
var settings = new JsonSerializerSettings() {
TypeNameHandling = TypeNameHandling.All
};
settings.Converters.Add(new TaskCreator(Data));
var json = JsonConvert.SerializeObject(this, settings);
// Reflection equivalent of JsonConvert.DeserializeObject<T>(json, settings);
var expectedParameters = new Type[] { typeof(string), typeof(JsonSerializerSettings) };
var method = typeof(JsonConvert).GetMethods().Where(mi => mi.IsGenericMethod && mi.IsStatic && mi.IsPublic && mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(expectedParameters)).Single();
return method.MakeGenericMethod(this.GetType()).Invoke(null, new object[] { json, settings });
}
}
Задачи могут «соглашаться» на хранение данных приложения или нет, поэтому может выглядеть примерно так:
public class NoDataTask : Task {
public NoDataTask() { }
}
public class DataTask : Task {
public DataTask(IApplicationData data) : base(data) { }
}
Я реализовал CustomCreationConverter
для создания нового экземпляра соответствующего класса при десериализации из JSON (вы могли заметить, что это используется в реализации Clone()
в базе Task
класс, показанный выше.
public class TaskCreator : CustomCreationConverter<Task> {
//public TaskCreator() { } // uncomment to try using converter with JsonProperty attribute in Project
private readonly IApplicationData _data;
public TaskCreator(IApplicationData data) {
_data = data;
}
public override Task Create(Type objectType) {
var hasDataConstructor = objectType.GetConstructor(new Type[] { typeof(IApplicationData) }) != null;
return hasDataConstructor ? (Task)Activator.CreateInstance(objectType, _data) : (Task)Activator.CreateInstance(objectType);
}
}
Это работает точно так, как требуется в методе Clone()
, полученное objectType
относится к DerivedClass (DataTask
в приведенном ниже примере)
var data = new ApplicationData();
var dataTask = new DataTask(data);
var dataTaskCloneData = ((DataTask)dataTask.Clone()).Data; // still has data intact - excellent
Однако я не уверен, как это сделать в случае сохранения задач. В настоящее время у меня есть класс Project
, содержащий List<ITask>
, который я сериализую / де-сериализую. Это отлично работает в отношении данные в каждой задаче, однако мне не удалось ввести ApplicationData
в десериализованные экземпляры задач.
[DataContract]
public class Project {
[DataMember]
//[JsonProperty(ItemConverterType = typeof(TaskCreator))] // uncomment to force use of converter
public List<ITask> Tasks { get; set; }
}
var project = new Project {
Tasks = new List<ITask> {
new NoDataTask(),
new DataTask(data)
}
};
var serialiserSettings = new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.All
};
serialiserSettings.Converters.Add(new TaskCreator(data));
var json = JsonConvert.SerializeObject(project, serialiserSettings);
var projectCopy = JsonConvert.DeserializeObject<Project>(json, serialiserSettings);
var projectCopyTask2Data = projectCopy.Tasks[1].Data; // data is null - bad
Я обнаружил, что из-за проекта с List<ITask>
преобразователь не используется. Я мог бы добавить преобразователь CustomCreationConverter<ITask>
, но в любом случае objectType
, переданный преобразователю, всегда имеет тип ITask
, тогда как мне нужен производный класс, чтобы иметь возможность создать соответствующий новый экземпляр.
Добавление атрибут [JsonProperty]
обеспечивает возможность использования конвертера как есть, но я не знаю метода, который я могу применить без него, используя конструктор без параметров, который бесполезен с учетом моей реализации, поскольку IApplicationData
всегда будет быть нулевым.
. NET Пример скрипта здесь - https://dotnetfiddle.net/WdyfDv