Я знаю, що існує атрибут для обробки приватних установників, але я начебто хочу, щоб ця поведінка була за замовчуванням, чи є спосіб досягти цього? За винятком налаштування джерела. Було б чудово, якби для цього було налаштування.
Відповіді:
Я прийшов сюди, шукаючи фактичний атрибут, який змушує Json.NET заповнювати властивість лише для читання при десериалізації, і це просто [JsonProperty], наприклад:
[JsonProperty]
public Guid? ClientId { get; private set; }
Просто надайте конструктор, який має параметр, що відповідає вашому властивості:
public class Foo
{
public string Bar { get; }
public Foo(string bar)
{
Bar = bar;
}
}
Зараз це працює:
string json = "{ \"bar\": \"Stack Overflow\" }";
var deserialized = JsonConvert.DeserializeObject<Foo>(json);
Console.WriteLine(deserialized.Bar); // Stack Overflow
Я віддаю перевагу такому підходу, де це можливо, оскільки:
{ get; private set; }і просто { get; }.{get;private set;}, а не з{get;}
{ get; }якщо тип має конструктор з параметром, що відповідає імені властивості.
Для цього я написав вихідний дистрибутив NuGet, який встановлює один файл із двома спеціальними засобами вирішення контрактів:
Встановіть NuGet:
Install-Package JsonNet.PrivateSettersContractResolvers.Source
Тоді просто використовуйте будь-який із засобів розв’язання:
var settings = new JsonSerializerSettings
{
ContractResolver = new PrivateSetterContractResolver()
};
var model = JsonConvert.DeserializeObject<Model>(json, settings);
Ви можете прочитати про це тут: http://danielwertheim.se/json-net-private-setters-nuget/
Репозиторій GitHub: https://github.com/danielwertheim/jsonnet-privatesetterscontractresolvers
Є дві альтернативи, які можуть вирішити проблему.
Alt 1: Про десериалізатори
ContractResolver.DefaultMembersSearchFlags =
DefaultMembersSearchFlags | BindingFlags.NonPublic;
Параметр серіалізації за замовчуванням підтримує всі типи членів класу. Тому це рішення поверне всі типи приватних членів, включаючи поля. Мене цікавить лише підтримка приватних сетерів.
Alt2: Створіть власний ContractResolver:
Тому це кращі варіанти, оскільки ми просто перевіряємо властивості.
public class SisoJsonDefaultContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(
MemberInfo member,
MemberSerialization memberSerialization)
{
//TODO: Maybe cache
var prop = base.CreateProperty(member, memberSerialization);
if (!prop.Writable)
{
var property = member as PropertyInfo;
if (property != null)
{
var hasPrivateSetter = property.GetSetMethod(true) != null;
prop.Writable = hasPrivateSetter;
}
}
return prop;
}
}
Для отримання додаткової інформації прочитайте мій пост: http://danielwertheim.se/json-net-private-setters/
DefaultMembersSearchFlagsбуло застарілим .
{get; }НЕ еквівалентно { get; private set; }. За першим способом property.GetSetMethod(true)повертається nullі другий true. Це мене здивувало. Ви повинні мати, private set;щоб десериалізація працювала належним чином.
Відповідь Даніеля (Alt2) є на місці, але мені це потрібно було, щоб працювати як для приватних установників, так і для геттерів (я працюю з API, який насправді має кілька речей, лише для запису, наприклад user.password.) Ось з чим я закінчився:
public class NonPublicPropertiesResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
var prop = base.CreateProperty(member, memberSerialization);
if (member is PropertyInfo pi) {
prop.Readable = (pi.GetMethod != null);
prop.Writable = (pi.SetMethod != null);
}
return prop;
}
}
Зареєстровано таким чином:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
ContractResolver = new NonPublicPropertiesResolver()
};