#c# #json.net
#c# #json.net
Вопрос:
У меня есть несколько классов со свойствами и конструкторами только для чтения, например:
public class MyClass
{
public string Foo { get; }
public bool Bar { get; }
public MyClass(string foo);
public MyClass(string foo, bool bar);
}
Который будет сериализован, но DeserializeObject
завершится неудачей.
Newtonsoft.Json.Исключение JsonSerializationException : не удается найти конструктор для типа MyClass. Класс должен иметь либо конструктор по умолчанию, один конструктор с аргументами, либо конструктор, помеченный атрибутом JsonConstructor. Путь ‘…’, строка 1, позиция 1
Добавление JsonConstructor
атрибута будет работать, но иногда я не контролирую исходный код типов. Могу ли я указать конвертеру действовать так, как если бы атрибут был каким-то другим способом?
В идеале без выполнения каждого параметра вручную в a JsonConverter.ReadJson
для каждого рассматриваемого типа.
Комментарии:
1. У меня нет ответа на ваш вопрос, но в качестве обходного пути — ничто не заставляет вас десериализовать этот конкретный тип. Вы могли бы также десериализовать свой собственный тип или даже анонимный тип, а затем сопоставить результаты с фактическим целевым типом. Конечно, для этого потребуется написать некоторый код, но также и пользовательский конвертер json…
2. Ну, это очень похоже на выполнение каждого параметра для каждого типа… Вероятно, я мог бы написать общее решение с помощью отражения (я предполагаю, что Json. Net все равно делает внутренне). Но если вы можете сказать «используйте этот конструктор
typeA, typeB
» или «притворись, что у него есть этот атрибут x для члена y», я бы предпочел не изобретать.
Ответ №1:
Провел небольшое исследование, и, похоже, вы можете использовать custom JsonContractResolver
во время десериализации, что обеспечит сериализатор правильным создателем объекта.
Итак, десериализация выглядит так:
var settings = new JsonSerializerSettings { ContractResolver = new MyContractResolver() };
var myClass = JsonConvert.DeserializeObject<MyClass>("{ "Foo": "Hello", "Bar": true }", settings);
И MyContractResolver
является:
public class MyContractResolver : DefaultContractResolver
{
public override JsonContract ResolveContract(Type type)
{
var contract = base.ResolveContract(type);
if (type == typeof(MyClass) amp;amp; contract is JsonObjectContract objectContract)
{
objectContract.OverrideCreator = (args) => new MyClass((string)args[0], (bool)args[1]);
var overrideConstructor = typeof(MyClass).GetConstructor(new[] { typeof(string), typeof(bool) });
foreach (var param in CreateConstructorParameters(overrideConstructor, objectContract.Properties))
objectContract.CreatorParameters.Add(param);
}
return contract;
}
}
Конечно, это очень схематично, и JsonSerializer
атрибут гораздо удобнее использовать. Однако, если у вас есть устаревшие классы, это может помочь.
Идея заключается в том, что при Serializer
заполнении JsonContract
для вашего типа — позвольте определителю контрактов по умолчанию создать его, а затем внедрить конструктор, который вам действительно нужно вызвать в OverrideCreator
свойстве contract, и описание его аргументов CreatorParameters
.
Комментарии:
1. Кажется, подход ставки. Огляделся в этом коде, есть переопределяемый
CreateObjectContract
. На самом деле он используетсяGetAttributeConstructor
для поиска атрибута, но, похоже, он не переопределяется, похожеDefaultContractResolver
, обрабатывает кучу атрибутов.