Приватні установники в Json.Net


93

Я знаю, що існує атрибут для обробки приватних установників, але я начебто хочу, щоб ця поведінка була за замовчуванням, чи є спосіб досягти цього? За винятком налаштування джерела. Було б чудово, якби для цього було налаштування.


1
Я шукав ту чи іншу відповідь.
marbel82

Відповіді:


112

Я прийшов сюди, шукаючи фактичний атрибут, який змушує 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; }.

19
Лише крихітна нотатка: це працює з {get;private set;}, а не з{get;}
timtam

8
Тільки невелике оновлення. Тепер він також працює з {get;};
Hav

1
@Hav Яка це версія з тих пір? Я щойно випробував v11.0.2, і він не працює {get;}
timtam

1
@tymtam Я думаю, що це працює, лише { get; }якщо тип має конструктор з параметром, що відповідає імені властивості.
Saeb Amini

2
@tymtam оновив відповідь цією альтернативою та прикладом.
Saeb Amini

77

Оновлено, нова відповідь

Для цього я написав вихідний дистрибутив NuGet, який встановлює один файл із двома спеціальними засобами вирішення контрактів:

  • PrivateSetterContractResolver
  • PrivateSetterCamelCasePropertyNamesContractResolver

Встановіть 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/


2
Посилання на ваш пост daniel.wertheim.se/2010/11/06/json-net-private-setters
Jafin

1
@Jafin url мертвий, danielwertheim.wordpress.com/2010/11/06/… вже зараз
Chris Marisic

1
Схоже, сьогодні Alt 2 - це безперечно шлях. DefaultMembersSearchFlagsбуло застарілим .
Тодд Меньє,

4
З c # 6, {get; }НЕ еквівалентно { get; private set; }. За першим способом property.GetSetMethod(true)повертається nullі другий true. Це мене здивувало. Ви повинні мати, private set;щоб десериалізація працювала належним чином.
emragins

Схоже, що зараз слід використовувати Install-Package JsonNet.ContractResolvers. github.com/danielwertheim/jsonnet-contractresolvers
Вирівняно

14

Відповідь Даніеля (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()
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.