Частные сеттеры в Json.Net

95

Я знаю, что есть атрибут для обработки частных установщиков, но я хочу, чтобы это поведение использовалось по умолчанию, есть ли способ сделать это? За исключением настройки источника. Было бы здорово, если бы для этого была настройка.

Даниэль
источник
1
Я искал тот или иной ответ.
marbel82

Ответы:

115

Я пришел сюда в поисках фактического атрибута, который заставляет Json.NET заполнять свойство readonly при десериализации, и это просто [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;}
Небольшое
8
Просто небольшое обновление. Теперь он также работает с {get;};
Hav
1
@Hav С какой это версии? Я только что протестировал версию 11.0.2, и она не работает {get;}
tymtam 06
1
@tymtam Я думаю, что это работает, только { get; }если у типа есть конструктор с параметром, соответствующим имени свойства.
Саеб Амини
2
@tymtam обновил ответ этой альтернативой и примером.
Саеб Амини
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

Старый ответ (еще действующий)

Есть две альтернативы, которые могут решить проблему.

Альтернативный вариант 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
Ссылка
Jafin
1
@Jafin url мертв, danielwertheim.wordpress.com/2010/11/06/… теперь есть
Крис Марисич
1
Похоже, что Alt 2 определенно подходит для наших дней. DefaultMembersSearchFlagsбыло устаревшим .
Todd Menier
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

Ответ @ Daniel (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()
};
Тодд Менье
источник