при этом
JsonConvert.DeserializeObject(value, type);
Создан новый объект с заданным type
для десериализации. Каково мое требование, я хочу использовать уже созданный объект для десериализации
CustomObject obj = new CustomObject(); //passing constructor args
JsonConvert.DeserializeObject(value, obj);
Но эта перегрузка недоступна.
Итак, есть ли обходной путь, чтобы я мог использовать уже созданный объект вместо создания нового типа через Deserializer .?
Почему я хочу это сделать? (Detail)
Я пишу обобщенный преобразователь из JSON в классы c #. Структура JSON выглядит как
{
"ActionType" : "LogEvent", //this is actually class name in a specific namespace
"Settings" : {
"FailureRetries" : 4,
"LogChannel" : 1234 // log channel id, implementation detail
}
}
То, что я делаю в маппере конфигурации, выглядит следующим образом
public void IAction GetActionFromConfig(){
dynamic config = configManager.Load<dynamic>(); //loads JSON file.
string className = config.ActionType; //from config we will get file name.
Type type = Type.GetType(nameSpaceForConfig + "." + className);
string settingValue = JsonConvert.SerializeObject(config.Settings);
object actionObject = JsonConvert.DeserializeObject(settingValue, type);
return (IAction)actionObject; // return the IAction from config.
}
Моя проблема в том, что классы реализации IAction
могут иметь некоторые зависимости в конструкторе (например, ILogger
), и по умолчанию десериализация не инициализирует параметр конструктора. Я знаю, что могу переопределить JsonConverter
для инициализации конструктора, но так как я пишу универсальный маппер, я не могу дать конкретные аргументы, используя custom JsonConverter
.
Вначале я планирую создать словарь, который будет сопоставлять Type
(c # Type
class) с конкретной реализацией для разрешения зависимостей.
Во-вторых, я получу конструкторы с заданным типом действия (тип указан в конфигурации с параметром ActionType
)
Type type = Type.GetType(nameSpaceForConfig + "." + className);
var ctors = typeof(type).GetConstructors();
var ctor = ctors[0];
List<Type> constructorArguments = new List<Type();
foreach (var param in ctor.GetParameters())
{
constructorArguments.Add(param.ParameterType);
}
Затем я сделаю запрос к словарю, созданному на шаге 1., чтобы получить аргументы для конструктора.
Наконец, я буду использовать Activator.CreateInstance()
(передавая зависимости конструктора, полученные на предыдущем шаге).
А затем используйте этот объект для десериализации для инициализации открытых свойств через файл конфигурации.
Так вот почему я хочу, чтобы десериализация произошла на object
, а не на type
.
ПРИМЕЧАНИЕ: Предварительное условие - каждое действие должно иметь один конструктор со всеми зависимостями.
в сторону: configManager.Load<T>
реализация
public T Load<T>() where T : class, new() => Load(typeof(T)) as T;
public object Load(Type type)
{
if (!File.Exists(_configurationFilePath))
return Activator.CreateInstance(type);
var jsonFile = File.ReadAllText(_configurationFilePath);
return JsonConvert.DeserializeObject(jsonFile, type, JsonSerializerSettings);
}